This is a Python microservice created using FastAPI that provides an Authentication REST API for the SciGateway web application.
This microservice requires an ICAT server to run against.
- Docker and Docker Compose installed (if you want to run the microservice inside Docker)
- Python 3.11 and Poetry installed on your machine (if you are not using Docker)
- ICAT server to connect to
- Private and public key pair (must be OpenSSH encoded) for encrypting and decrypting the JWTs
- This repository cloned
-
Create a
.env
file alongside the.env.example
file. Use the example file as a reference and modify the values accordingly.cp scigateway_auth/.env.example scigateway_auth/.env
-
Create a
logging.ini
file alongside thelogging.example.ini
file. Use the example file as a reference and modify it accordingly:cp scigateway_auth/logging.example.ini scigateway_auth/logging.ini
-
Generate OpenSSH encoded private and public key pair in the
keys
directory:ssh-keygen -b 2048 -t rsa -f keys/jwt-key -q -N "" -C ""
Ensure that Docker is installed and running on your machine before proceeding.
The easiest way to run the application with Docker for local development is using the docker-compose.yml
file. It is
configured to start the application in a reload mode which using the mounted scigateway_auth
directory means that
FastAPI will watch for changes moade to the code and automatically reload the application on the fly.
-
Build and start the Docker container:
docker compose up
The microservice should now be running inside Docker at http://localhost:8000 and its Swagger UI could be accessed at http://localhost:8000/docs.
Use the Dockerfile
's dev
stage to run just the application itself in a container. Use this only for local
development (not production)! Mounting the scigateway_auth
directory to the container via a volume means that FastAPI
will watch for changes made to the code and automatically reload the application on the fly.
-
Build an image using the
Dockerfile
'sdev
stage from the root of the project directory:docker build --file Dockerfile --target dev --tag scigateway-auth:dev .
-
Start the container using the image built and map it to port
8000
locally:docker run \ --publish 8000:8000 \ --name scigateway-auth \ --volume ./scigateway_auth:/app/scigateway_auth \ --volume ./keys/jwt-key:/app/keys/jwt-key \ --volume ./keys/jwt-key.pub:/app/keys/jwt-key.pub \ --volume ./maintenance/maintenance.json:/app/maintenance/maintenance.json \ --volume ./maintenance/scheduled_maintenance.json:/app/maintenance/scheduled_maintenance.json \ scigateway-auth:dev
or with values for the environment variables:
docker run \ --publish 8000:8000 \ --name scigateway-auth \ --env AUTHENTICATION__REFRESH_TOKEN_VALIDITY_DAYS=14 \ --volume ./scigateway_auth:/app/scigateway_auth \ --volume ./keys/jwt-key:/app/keys/jwt-key \ --volume ./keys/jwt-key.pub:/app/keys/jwt-key.pub \ --volume ./maintenance/maintenance.json:/app/maintenance/maintenance.json \ --volume ./maintenance/scheduled_maintenance.json:/app/maintenance/scheduled_maintenance.json \ scigateway-auth:dev
The microservice should now be running inside Docker at http://localhost:8000 and its Swagger UI could be accessed at http://localhost:8000/docs.
Use the Dockerfile
's test
stage to run the tests in a container. Mounting the scigateway_auth
and test
directories to the container via volumes means that any changes made to the application or test code will automatically
be synced to the container next time you run the tests.
-
Build an image using the
Dockerfile
'stest
stage from the root of the project directory:docker build --file Dockerfile --target test --tag scigateway-auth:test .
-
Run the tests using:
docker run \ --rm \ --name scigateway-auth \ --volume ./scigateway_auth:/app/scigateway_auth \ --volume ./test:/app/test \ scigateway-auth:test
Ensure that Python and Poetry are installed on your machine before proceeding. To install Poetry, follow the instructions from their documentation.
-
Install the project's dependencies:
poetry install
-
Start the application:
fastapi dev scigateway_auth/main.py --host 0.0.0.0 --port 8000
The microservice should now be running in development mode at http://localhost:8000 and its Swagger UI could be accessed at http://localhost:8000/docs. FastAPI will watch for changes made to the code and automatically reload the application on the fly.
The configuration for the application is handled
using Pydantic Settings. It allows for loading config
values from environment variables or the .env
file. Please note that even when using the .env
file, Pydantic will
still read environment variables as well as the .env
file, environment variables will always take priority over values
loaded from the .env
file.
Listed below are the environment variables supported by the application.
Environment Variable | Description | Mandatory | Default Value |
---|---|---|---|
API__ROOT_PATH |
(If using a proxy) The path prefix handled by a proxy that is not seen by the app. | No | |
API__ALLOWED_CORS_HEADERS |
The list of headers that are allowed to be included in cross-origin requests. | Yes | |
API__ALLOWED_CORS_ORIGINS |
The list of origins (domains) that are allowed to make cross-origin requests. | Yes | |
API__ALLOWED_CORS_METHODS |
The list of methods that are allowed to be used to make cross-origin requests. | Yes | |
AUTHENTICATION__PRIVATE_KEY_PATH |
The path to the private key to be used for encoding JWT access and refresh tokens. | Yes | |
AUTHENTICATION__PUBLIC_KEY_PATH |
The path to the public key to be used for decoding JWT access and refresh tokens signed by the corresponding private key. | Yes | |
AUTHENTICATION__JWT_ALGORITHM |
The algorithm to use to decode and encode the JWT access and refresh tokens. | Yes | |
AUTHENTICATION__ACCESS_TOKEN_VALIDITY_MINUTES |
Minutes after which the JWT access token expires. | Yes | |
AUTHENTICATION__REFRESH_TOKEN_VALIDITY_DAYS |
Days after which the JWT refresh token expires. | Yes | |
AUTHENTICATION__JWT_REFRESH_TOKEN_BLACKLIST |
The list of blacklisted JWT refresh tokens which when received will reject to refresh the provided access token. | Yes | |
AUTHENTICATION__ADMIN_USERS |
The list of admin users. These are the ICAT usernames of the users normally in the <icat-mnemonic>/<username> form. |
Yes | |
MAINTENANCE__MAINTENANCE_PATH |
The path to the json file containing the maintenance state. |
Yes | |
MAINTENANCE__SCHEDULED_MAINTENANCE_PATH |
The path to the json file containing the scheduled maintenance state. |
Yes | |
ICAT_SERVER__URL |
The URL to the ICAT server to connect to. | Yes | |
ICAT_SERVER__CERTIFICATE_VALIDATION |
Whether to verify ICAT certificates using its internal trust store or disable certificate validation completely. | Yes | |
ICAT_SERVER__REQUEST_TIMEOUT_SECONDS |
The maximum number of seconds that the request should wait for a response from ICAT before timing out. | Yes |
The AUTHENTICATION__JWT_REFRESH_TOKEN_BLACKLIST
environment variable holds the list of blacklisted JWT refresh tokens
which when received will reject to refresh the provided access token. This means that you can add or remove a JWT
refresh token from the blacklist by adding or removing the token from the environment variable.
PLEASE NOTE Changes made to the AUTHENTICATION__JWT_REFRESH_TOKEN_BLACKLIST
environment variable require the
application/container to be restarted in order for them to take effect. This is because the values for the environment
variable gets loaded once at startup.
The AUTHENTICATION__ADMIN_USERS
environment variable holds the list of admin users which are the ICAT usernames of
the users normally in the <icat-mnemonic>/<username>
form. This means that you can add or remove an admin user by
adding or removing their username environment variable.
PLEASE NOTE Changes made to the AUTHENTICATION__ADMIN_USERS
environment variable require the application/container
to be restarted in order for them to take effect. This is because the values for the environment variable gets loaded
once at startup.
The maintenance
folder at the root of the project directory contains two json files which return the appropriate state
of the system. This means that you can edit the values in the files in accordance with the desired state of the system.
PLEASE NOTE Changes made to maintenance.json
and scheduled_maintenance.json
file using vim do not get synced
in the Docker container because it changes the inode index number of the file. A workaround is to create a new file
using the maintenance.json
or scheduled_maintenance.json
file, apply your changes in the new file, and then
overwrite the maintenance.json
/ scheduled_maintenance.json
file with the content of the new file, see below an
example for maintenance.json
file.
cp maintenance/maintenance.json new_maintenance.json
vim new_maintenance.json
cat new_maintenance.json > maintenance/maintenance.json
rm new_maintenance.json
This repository contains a Nox file (noxfile.py
) which exists in the root level of this
repository. There are a handful of sessions which help with repetitive tasks during development. To install Nox, use the
following command:
pip install --user --upgrade nox
To run a specific Nox session, use the following:
nox -s [SESSION NAME]
Currently, the following Nox sessions have been created:
black
- this uses Black to format Python code to a pre-defined style.lint
- this uses flake8 with a number of additional plugins (see the includednoxfile.py
to see which plugins are used) to lint the code to keep it Pythonic..flake8
configuresflake8
and the plugins.safety
- this uses safety to check the dependencies (pulled directly from Poetry) for any known vulnerabilities. This session gives the output in a full ASCII style report.tests
- this uses pytest to execute the automated tests intest/
.
To make use of Git's ability to run custom hooks, pre-commit is used. Pip is used to install this tool:
pip install --user --upgrade pre-commit
This repo contains an existing config file for pre-commit
(.pre-commit-config.yaml
) which needs to be installed
using:
pre-commit install
When you commit work on this repo, the configured commit hooks will be executed, but only on the changed files. This is good because it keeps the process of committing a simple one, but to run the hooks on all the files locally, execute the following command:
pre-commit run --all-files