This project is in support of efforts to hire python engineers.
Ensure you can run the application locally by cloning it.
These directions assume you will use poetry
for dependency and environment management.
poetry install
Create a file named .env in your project root. Contents:
SECRET_KEY=mysecret
DATABASE_URI=sqlite:///users.db
TEST_DATABASE_URI=sqlite:///:memory:
FLASK_ENV=development
Note: Changes to the values in this file will be cached, so be sure to restart the application to get new values.
This file resides in the /project root/app folder. Notice the following function accepts two arguments:
os.getenv(key, default value)
Depending on how secure you want your application to be, you may wish to modify the second argument to be less revealing, like so:
SECRET_KEY = os.getenv("SECRET_KEY", "mysecret")
# would change to:
SECRET_KEY = os.getenv("SECRET_KEY", "notmysecret")
Find the .gitignore file. It is one folder "up" from your project root folder. In that file, look for the # Environments section. It should have at least the exclusions you see here:
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
You many need to install the pre-commit project
Then run
pre-commit run --all-files
You should get output showing "Passed" or (for json files) "Skipped"
trim trailing whitespace.................................................Passed
fix end of files.........................................................Passed
check yaml...............................................................Passed
check json...........................................(no files to check)Skipped
black....................................................................Passed
There are a couple of automated tests to run.
poetry run pytest --verbose
You should see something similar to:
============================================================================================= test session starts ==============================================================================================
platform darwin -- Python 3.12.6, pytest-8.3.3, pluggy-1.5.0 -- /Users/boyd.hemphill/Library/Caches/pypoetry/virtualenvs/project-python-flask-mx59eYov-py3.12/bin/python
cachedir: .pytest_cache
rootdir: /Users/boyd.hemphill/code/project-python-flask
configfile: pyproject.toml
testpaths: tests
collected 3 items
tests/test_auth.py::test_register_user PASSED [ 33%]
tests/test_auth.py::test_login_user PASSED [ 66%]
tests/test_auth.py::test_login_invalid_user PASSED [100%]
============================================================================================== 3 passed in 0.03s ===============================================================================================
Some tips on managing the SQLite database in the beginning and any time the database structure is changed in the models.py file.
To initialize the SQLite database, you'll need to use Flask-Migrate:
set FLASK_APP=app.py
export FLASK_APP=app.py
flask db init
This command creates a migrations
directory with the necessary files for managing database migrations.
flask db migrate -m "Initial migration"
This command generates a new migration script based on the changes detected in your models.
Apply the migration to create the database and tables:
flask db upgrade
This command applies the migration and creates the database file (users.db) if it doesn't exist, along with the tables defined in your models.
users.db
file in your instance
folder.After following these steps, your SQLite database should be initialized and ready to use. The tables defined in your models.py
file will be created in the database.
Whenever you make changes to your models, you'll need to create a new migration and apply it:
flask db migrate -m "Description of changes"
flask db upgrade
Once you update models.py file with any schema changes:
flask db migrate -m "Added roles tables for many-to-many relationships"
migrations/versions/
and ensure it correctly captures all your intended changes.flask db upgrade
export FLASK_ENV=development # use the development settings
poetry run python app.py
You should see something like this:
/code/project-python-flask >poetry run python run.py
* Serving Flask app 'app'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
* Debugger is active!
* Debugger PIN: 413-423-614
Create a user that will allow you to authenticate. For ease of using the project you submit, please do not change the credentials.
curl \
-X POST http://127.0.0.1:5000/register \
-H "Content-Type: application/json" \
-d '{"username":"Dev Userson", "email":"dev.userson@example.com", "password":"sosecure"}'
Show that the user can log in:
curl -X POST http://127.0.0.1:5000/login \
-H "Content-Type: application/json" \
-d '{"email":"dev.userson@example.com", "password":"sosecure"}'
See API call below titled "SHOW ALL USERS with ALL ROLES"
# REGISTER
curl -X POST http://127.0.0.1:5000/register \
-H "Content-Type: application/json" \
-d '{"username":"Dev Userson", "email":"dev.userson@example.com", "password":"sosecure"}'
# LOGIN
curl -X POST http://127.0.0.1:5000/login \
-H "Content-Type: application/json" \
-d '{"email":"dev.userson@example.com", "password":"sosecure"}'
# TOGGLE ACTIVE
curl -X POST http://127.0.0.1:5000/toggle-active \
-H "Content-Type: application/json" \
-d '{"email":"dev.userson@example.com"}'
# SHOW USER PROFILE
curl -X POST http://127.0.0.1:5000/profile \
-H "Content-Type: application/json" \
-d '{"username":"Scott Swain", "email":""}'
# SHOW ALL USERS (deprecated)
curl -X GET http://127.0.0.1:5000/users \
-H "Content-Type: application/json"
# SHOW ALL USERS with ALL ROLES
curl -X GET http://127.0.0.1:5000/users-roles \
-H "Content-Type: application/json"
# ACCESS REPORT
# (Note: can replace "all_users" below with "active_users" or "inactive_users")
curl -X POST http://127.0.0.1:5000/access-report \
-H "Content-Type: application/json" \
-d '{"limit_to":"all_users"}'
# DELETE USER
curl -X POST http://127.0.0.1:5000/delete-user \
-H "Content-Type: application/json" \
-d '{"email":"dev.userson@example.com"}'
# SHOW ALL ROLE(S)
curl -X GET http://127.0.0.1:5000/roles-show \
-H "Content-Type: application/json"
# CREATE ROLE(S)
curl -X POST http://127.0.0.1:5000/create-roles \
-H "Content-Type: application/json" \
-d '{"roles_depts":["Senior Dev,Getting Started", "Dev,Getting Started"]}'
# ASSIGN ROLE(S)
# (Note: any number of users can be assigned any number of role/dept combinations.)
curl -X POST http://127.0.0.1:5000/assign-roles \
-H "Content-Type: application/json" \
-d '{"emails_roles_depts":["dev.userson@example.com,Senior Dev,Getting Started", "scott@oceanmedia.net,Dev,Getting Started", "scott@oceanmedia.net,Dev,Finance Dept"]}'
For each task please follow this process:
:information_source: - All input and response should be assumed to be via curl
. There is no expectation of a front end being created for this project.
:warning: - Please manage your time. We expect between three and four hours of effort and the associated quality. If you would like to take more time you are welcome to do so, but it is in no way required.
Most importantly, have fun. Show off a bit. Push an opinion or two forward to spark a discussion.
The following are "required." We recommend working these first and expect they will take between 2 and 3 hours to complete. If you do not have time to complete them all that is fine. Ensure you have a priority list and are able to discuss why you prioritized them as you did.
Create a Github action that runs the linting and automated tests. It is a Victory goal that humans don't look at code that is not linted to standard and passing tests unless there is a specfic reason (e.g. a draft pull request).
As an application administrator I want to be able to mark a user as "inactive" so that they cannot log in or perform any actions on the system.
Acceptance Criteria
As a compliance officer I want to be able to check a report that tells me who my users are and what their access level is so that I can conduct the monthly reviews and drive remediation as required by SOC 2 Type 2.
Accpetance Criteria
As an application admin I need to assign each user one or more roles so that they have a clear and predictable set of permissions. I do not need to override roles at this time.
Acceptance Criteria
As a compliance officer I want our credentials and other sensitive information removed from source control and stored according to OWASP best practices so that our operations are hardened against external and internal attacks.
In config.py
there are secrets stored in plain text.
:information_source: - See app/routes/user_routes.py
Write a detailed Github issue explaining your understanding of the requirements as you infer them. Write them as if you will not be the one to work on the task.
If you have some extra time and would like to show off a bit, the following are some suggestions to feed your creativity. You are encouraged to come up with, document and solve your own problem. Victory places a high value on engineers who work to ensure team efficiency.
Think outside the box for all of this. For example maybe instead of fixing something you'd rather write out a list of several tickets and prioritize them. In this case we will discuss what you are suggesting, approaches and the like.
Alternatively you could find one thing that shows off a particular passion and dive as deep as you like.
Or, you could come up approach we have not thought of. Think of this as a way to shape your interiew towards your interests and passion.
:information_source: - If you decide to tackle something and run out of time, simply make a PR but don't merge it. Note this in your email telling us where to find your project and it becomes a conversation piece for the final interview.
There are many issues of security with our application. You've seen above how we like to communicate tasks, so:
Our build does some linting with pre-commit
and runs our tests. If this is a project you are responsible for what would you add?
Remember that writing the ticket is 60% of what we are looking for here. Only implement if this is something you are passionate about.
SQLlite is great for something like an interview project. Most Victory projects are backed by Postgres or MySQL.
Show us the lack of print statements in your code and how you go about tracking down values of variables and the like when looking for defects in the code.
The people evaluating your submission will do the following before we schedule the final interview.
On a global level, we are evaluating you on your ability
We will do the following:
Within five business days we will respond to you to schedule a 90 minute in-person interview with Victory staff or a note to let you know we are going in a different direction.
To prepare for the interview be ready to
Please note that folks on the team are often taking a position counter to your own. It is of primary importance that members of Victory teams can disagree, discuss and come to a common understanding and path forward.
You should walk away with a solid understanding of what it is like to work on a Victory team on a daily basis. We understand that you may not want to work the way we do. Better for all concerned if we figure that out as early as possible.
We are going to make six figure bet on you. You are going to put your career in our hands and expect us to help you grow professionally.
We deeply appreciate the time you are taking to ensure joining Victory is of benefit to all concerned (yourself, Victory and our clients).