Skip to content

Django REST API + Gunicorn + Docker + SSL + Token based authentication

License

Notifications You must be signed in to change notification settings

reljicd/django-rest-prod-ready-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Django REST API + Gunicorn + Docker + SSL + Token based authentication

Solution

Diagram.jpg

Solution was provided using Python 3.11, Django, Django REST, Gunicorn and database is SQLite.

Testing was done using Django flavor of untitest module.

Solution comes included with Django Admin Site. Using admin site it is possible to register new users and generaly to browse database and make CRUD operations on provided tables.

Screenshot 2023-04-26 at 20.16.20.png Screenshot 2023-04-26 at 20.15.51.png Screenshot 2023-04-26 at 20.17.40.png

I also provided the access to REST Browsable API.

Screenshot 2023-04-26 at 20.15.28.png Screenshot 2023-04-26 at 20.17.29.png

Security

Authentication

API Authentication is provided using Django REST Knox library.

User of the REST API is supposed to login using his username and password in order to obtain token. Token is then used to authenticate API endpoints.

Knox provides one token per call to the login view - allowing each client to have its own token which is deleted on the server side when the client logs out. Knox also provides an optional setting to limit the amount of tokens generated per user.

Knox tokens are only stored in an encrypted form. Even if the database were somehow stolen, an attacker would not be able to log in with the stolen credentials.

SSL/TLS

I generated self signed certificate and key using OpenSSL library for Gunicorn's SSL transport.

Other secrets

Superuser password and Django secret key are also provided as textual files that can be modified.

All the secrets are in the secrets folder.

In real production these secrets would be used in CI/CD in encrypted form and services would use some secret management service. They would never be passed around in plain text like this

URLs

Development Server: http://127.0.0.1:8080

Production Server: https://127.0.0.1:443

Endpoints

Defined in urls.py and urls.py

  • /admin/ - Django Admin Site
  • /api/clicks/campaign/:id/ - Campaign (with provided id) clicks
  • /api/browser/ - REST Browser
  • /api/auth/login - Login endpoint
  • /api/auth/logout - Logout endpoint
  • /api/auth/logoutall - Invalidate all tokens endpoint

REST API

Login

  • POST /api/auth/login

Required headers

  • Authorization: Basic username:password -

Credentials should be encoded using base64. Example in tests.py:

base64.b64encode(f'{USERNAME}:{PASSWORD}'.encode()).decode()

Clicks

  • GET /api/clicks/campaign/:id[int]/?after_date=:after_date[str]&before_date=:before_date[str]

Path Parameters

  • id - campaign ID

Query Parameters

  • after_date - After date string in the format "YYYY-MM-DD HH:MM:SS"
  • before_date - Before date string in the format "YYYY-MM-DD HH:MM:SS"

Required headers

  • Authorization: Token token_value

Example:

https://127.0.0.1/api/clicks/campaign/4510461/?after_date=2021-11-07+03:10:00&before_date=2021-11-07+03:30:00

Prerequisites for CLI and Make

Install virtual environment:

$ python -m virtualenv env

Activate virtual environment:

On macOS and Linux:

$ source env/bin/activate

On Windows:

$ .\env\Scripts\activate

Install dependencies:

$ pip install -r requirements.txt

How to run

Make

This is the easiest way to run.

For non docker commands execute Prerequisites.

All the commands are defined in the Makefile.

  • bootstrap - Bootstraps Django. Includes: clean, migrate, create_superuser, import_data, collectstatic

  • clean - Deletes db.sqlite3 and generated src/static folder.

  • migrate - Applies migration

  • create_superuser - Creates Django super user (admin)

  • import_data - Imports data/click_log.csv into SQLite

  • collectstatic - Generates static files to serve with Gunicorn and built in dev server

  • run_tests - Runs tests

  • run_dev_server - Runs development server on http://127.0.0.1:8080

  • run_prod_server - Runs Gunicorn server on https://127.0.0.1:443

  • docker_build - Builds Docker image

  • docker_run_tests - Runs tests using generated Docker image

  • docker_run_dev_server - Runs development server on http://127.0.0.1:8080 inside Docker container

  • docker_run_prod_server - Runs Gunicorn server on https://127.0.0.1:443 inside Docker container

All the commands are run from the root of the project in the form:

$ make command

Examples:

$ make bootstrap
$ make run_tests
$ make docker_run_prod_server

CLI

You can run the application from the command line with manage.py.

Before running any command first execute Prerequisites.

From the root of the project:

Run migrations:

$ python src/manage.py migrate

Import data/click_log.csv into SQLite:

$ python src/manage.py import_clicks_from_csv --path data/click_log.csv

Create Django super user (admin):

$ python src/manage.py createsuperuser --email admin@example.com --username admin

Generate static files to serve with Gunicorn and built in dev server:

$ python src/manage.py collectstatic

Run development server on port 8000:

$ python src/manage.py runserver

Run Gunicorn server on port 443, using SSL:

$ export DEBUG=False; export SECRET_KEY="${cat secrets/secret.key}"; cd src; \
	python -m gunicorn core.wsgi \
	-b :443 --keyfile ../secrets/key.pem --certfile ../secrets/cert.pem

Docker

It is also possible to run servers and tests using Docker.

Build the Docker image:

$ docker build -t reljicd/clicks_api --build-arg DJANGO_SUPERUSER_PASSWORD=very_secret_password -f docker/Dockerfile .

Run development server on http://127.0.0.1:8080 inside Docker container:

$ docker run -p 8000:8000 --rm reljicd/clicks_api manage.py runserver 0.0.0.0:8000

Run Gunicorn server on https://127.0.0.1:443 inside Docker container:

$ docker run -p 443:443 -e SECRET_KEY="${cat secrets/secret.key}" -e DEBUG=False --rm reljicd/clicks_api

Driving REST API using Postman

Screenshot 2023-04-26 at 18.21.00.png Screenshot 2023-04-26 at 18.21.26.png Screenshot 2023-04-26 at 22.04.03.png

Helper Tools

Django Admin

It is possible to add additional admin user who can login to the admin site. Run the following command:

$ python src/manage.py createsuperuser

Enter your desired username and press enter.

Username: admin_username

You will then be prompted for your desired email address:

Email address: admin@example.com

The final step is to enter your password. You will be asked to enter your password twice, the second time as a confirmation of the first.

Password: **********
Password (again): *********
Superuser created successfully.

Go to the web browser and visit http://127.0.0.1:8000/admin (dev) or https://127.0.0.1/admin (prod)

REST Browsable API

Go to the web browser and visit http://127.0.0.1:8000/api/browser/ (dev) or https://127.0.0.1/api/browser/ (prod)

Tests

Tests can be found in tests.py.

Before running tests using CLI or Make first execute Prerequisites.

Using manage.py:

$ python src/manage.py test clicks

Using make:

$ make run_tests

Docker

It is also possible to run tests using Docker. First build the image following instructions in Make or Docker

Using docker CLI:

$ docker run --rm reljicd/clicks_api manage.py test clicks

Using make:

$ make docker_run_tests