Publishing House application can be used to store authors and books data for edition purposes. This Final Project demonstrates my skills in:
- Coding in Python 3
- Relational Database Architecture
- Modeling Data Objects with SQLAlchemy
- Internet Protocols and Communication
- Developing a Flask API
- Authentication and Access
- Authentication with Auth0
- Authentication in Flask
- Role-Based Access Control (RBAC)
- Testing Flask Applications
- Deploying Applications
Python 3.9.5 from Official website
pip 21.1.1 from Official website
Create virtual environment for the application:
python3 -m venv env
Activate virtual environment with command:
source env/bin/activate
To deactivate:
deactivate
In the virtual environment install project dependencies from the requirements.txt file with pip command:
pip3 install -r requirements.txt
Export environment variables with:
source setup.sh
Start the postgresql server on ubuntu:
sudo service postgresql start
Now we need to create main and test databases:
sudo -u postgres createdb publishing_house
sudo -u postgres createdb test
Psql users and passwords can be edited in the setup.sh file.
Locally, the application will run on localhost:5000/
Production version URL is hosted on https://fsnd-capstone-publishing-house.herokuapp.com/
There are 3 roles:
Reader can access a list of books and authors. Permissions - get:authors, get:books.
Coordinator can see the details of books and authors, including a permission to modify and create new entries. Permissions - reader + patch:author, patch:book, post:author, post:book.
Editor has the major access to all the data with the coordinator access plus permissions to delete books and authors. Permissions - coordinator + delete:author, delete:book.
From the main folder with app.py file run the command on Lunix:
export FLASK_APP=app && export FLASK_ENV=development && flask run --reload
On Windows:
set FLASK_APP=app
set FLASK_ENV=development
set flask run --reload
For the login purposes this app is using 3d party authentication: Auth0 services. First, a user needs to sign up. That email needs to be assigned to a role in the auth0 dashboard. After the login auth0 returns a jwt token that is used for checking permissions. Each route, except the landing page, requires specific auth0 permission. Depending on those permissions, links to various routes become available.
To sign in as a reader:
email: reader_user@gmail.com
password: Reader_user0
as a coordinator:
email: coordinator_user@gmail.com
password: Coordinator_user0
as an editor:
email: editor_user@gmail.com
password: Editor_user0
All routes use render_template method to display the information. For testing purposes and possible further development, most of the routes are equipped with json object returned if the request is of json type.
Offers to log in, if a session token is available, redirects to the /callback route with the appropriate information displayed, according to the permissions. Also, logout button will clear session token and redirect to landing page.
Here the application will determine what permissions a user has: for a reader will display Books and Authors, as well as Logout and Main Page links.
For a coordinator and an editor there will be additional links to Add New Book and Add New Author available as well as Edit Book and Edit Author in the detailed pages.
And only an editor will have Delete buttons for Books and Authors displayed when followed the links to a precise book or author.
For readers this route will display just a list of books and authors.
For editors and coordinators this route will add links to detailed information, as well as editing options.
Autho0 error will be raised if the permission is not found.
JSON response will include:
{
"success": True,
"books": list // "authors": list
}
A reader won't have access to this route.
Coordinator will see
- the details of the book, its author and a button to edit it
- author details, his books and a route to edit it
Editor will get additional buttons to delete the book or the author.
If the book or author id is not in the database, 422 error will be raised.
JSON response will include:
{
"success": True,
"book": book.format() // "author": author.format()
}
A reader won't have access to this route. Auth0 will raise error if authorization header is missing.
Book and Author form objects will be populated with the existing entry on GET request. POST method will pick up the new data and return it to the database, updating the entry with the corresponding id.
If the book or author id is not in the database, 422 error will raised.
JSON response for POST request will include:
{
"success": True,
"book": book.format() // "author": author.format()
}
JSON response for GET request will include:
{
"success": True,
"permissions": permissions
}
A reader won't have access to this route.
On GET request a BookForm or AuthorFrom will be loaded, JSON response for GET request will include:
{
"success": True,
"permissions": permissions
}
On POST request the forms will store user's input in the database, redirecting to the /books and /authors routes respectively.
JSON response for POST request will include:
{
"success": True,
"author": author.format() // "book": book.format
}
Books can be created only with the authors from the database, so in order to add a new book, its author should be created already. If the author is not in the database, 404 error will raised.
This route authorized only for the editor role.
If the book or author id is not in the database, 404 error will raised. And return JSON object
{
"success": False
}
On successful deletion JSON object will be returned:
{
"success": True,
"book": book.format(), // "author": author.format()
"permissions": permissions
}
Auth0 errors: token_expired, invalid_claims, invalid_header.
Application errors:
- 400:Bad Request. The server couldn't process your request,
- 404:You are trying to access an item that is not in the database. The server can not find the requested resource.,
- 422:The request was well-formed but was unable to be followed due to semantic errors. The server couldn't process your request.
- 500:Internal server error. The server has encountered a situation it doesn't know how to handle.
Testing is done with unittest library, to run the tests:
python test_app.py
Tests check all endpoints for success and fail behaviors, as well as for RBAC with appropriate permissions and without them.
To check for pep8 errors I used pycodestyle library, excluding autogenerated folders:
pycodestyle . --exclude=/migrations,env
For clearing the errors I ran authopep8 program on each python file:
autopep8 --in-place --aggressive --aggressive <file_name>.py