This library contains the most common features used by the different Aether django modules.
This library requires Python 3.6 and above.
Python libraries:
- django As web framework. (Above 2.2)
- django-cors-headers for handling the server headers required for Cross-Origin Resource Sharing (CORS).
- django-debug-toolbar A configurable set of panels that display various debug information about the current request/response.
- django_postgrespool2 Postgres Connection Pooling for Django, powered by SQLAlchemy.
- django-prometheus To monitor the application with Prometheus.io.
- django-silk A live profiling and inspection tool for the Django framework.
- django-uwsgi Django related examples/tricks/modules for uWSGI.
- djangorestframework A powerful and flexible toolkit for building Web APIs. (Above 3.8)
- drf-dynamic-fields Dynamically select only a subset of fields per DRF resource, either using a whitelist or a blacklist.
- markdown This is a Python implementation of John Gruber's Markdown.
- psycopg2-binary Python-PostgreSQL Database Adapter.
- pygments A syntax highlighting package written in Python.
- python-json-logger A python library adding a json log formatter.
- requests HTTP for Humans.
- uwsgi The Python Web Server Gateway Interface.
Extra dependencies (based on settings):
-
cache
- django-cacheops A slick ORM cache with automatic granular event-driven invalidation.
- django-redis Full featured redis cache backend for Django.
-
scheduler
- django-rq A simple app that provides django integration for RQ (Redis Queue).
- redis The Python interface to the Redis key-value store.
- rq Simple, lightweight, library for creating background jobs, and processing them.
- rq-scheduler Small package that adds job scheduling capabilities to RQ.
-
server
- sentry-sdk Python client for Sentry.
-
storage
- django-minio-storage A django storage driver for minio.
- django-storages A collection of custom storage backends for Django. Enabled for boto3 and google-cloud-storage.
- django-cleanup Automatically deletes old file for FileField and ImageField. It also deletes files on models instance deletion.
-
test
- coverage A tool for measuring code coverage of Python programs.
- flake8 Tool For Style Guide Enforcement.
- flake8-quotes Flake8 extension for checking quotes in python.
- tblib Traceback serialization library.
-
webpack
- django-webpack-loader Transparently use webpack with django.
# standalone
pip3 install aether.sdk
# with extra dependencies
pip3 install aether.sdk[scheduler,server,storage,test,webpack]
How to create the package distribution
Execute the following command:
python3 setup.py bdist_wheel
or
./scripts/build.sh
How to test the library
First install dependencies (execute it only once):
./scripts/install.sh
After that execute the following command:
source ./venv/bin/activate
./scripts/test.sh
The file scripts/test.ini
contains the environment variables used in the tests.
Add this snippet in the settings.py
file to have the build the django application
settings based on the environment variables.
# if it's an aether module
from aether.sdk.conf.settings_aether import * # noqa
# if it's an external aether product
from aether.sdk.conf.settings import * # noqa
# continue with the application specific settings
# and re-import the settings variables you need to reuse
# from aether.sdk.conf.settings[_aether] import WHATEVER YOU NEED TO...
Add this snippet in the urls.py
file to generate default urlpatterns
based on the application settings.
from aether.sdk.conf.urls import generate_urlpatterns
urlpatterns = generate_urlpatterns(token=[True|False], app=[
# include here the application/module specific URLs
])
Default URLs included:
-
The health endpoints:
- the
/health
URL. Always responds with200
status and an empty content. Usesaether.sdk.health.views.health
view. - the
/check-db
URL. Responds with500
status if the database is not available. Usesaether.sdk.health.views.check_db
view. - the
/check-app
URL. Responds with current application version and more. Usesaether.sdk.health.views.check_app
view.
- the
-
the
/admin
section URLs (ADMIN_URL
setting). -
the
/admin/~prometheus/metrics
URL. Displays the raw monitoring data. -
the
/admin/~uwsgi/
URL. If uWSGI is running displays the server uWSGI settings. -
the
/admin/~purge-cache
URL. Purges django cache. Available if django cache is enabled. -
the
/admin/~realms
URL. Returns the list of realms with linked data. TheDEFAULT_REALM
is always included even if it has no linked data. If MULTITENANCY is not enabled returns the fake realmsettings.NO_MULTITENANCY_REALM
. -
the
/accounts
URLs (AUTH_URL
setting), checks if the REST Framework ones, using the templates indicated inLOGIN_TEMPLATE
andLOGGED_OUT_TEMPLATE
settings, or the Keycloak ones.
Based on the arguments:
-
token
: indicates if the application should be able to create and return user tokens via POST request and activates the URL. The URL endpoint is indicated in theTOKEN_URL
setting. Defaults to/token
. Usesaether.sdk.auth.views.auth_token
view.If the current user is not an admin user then creates and returns the authorization token for himself, otherwise creates a token for the
username
contained in the request payload.
Based on the application settings:
-
If
DEBUG
is enabled:- the
debug toolbar
URLs.
- the
-
If
PROFILING_ENABLED
is set:- the
/admin/~silk/
URL. Displays the profiling data.
- the
-
If
EXTERNAL_APPS
is set and valid:-
the
/check-app/{name}
URL. Checks if the external application is reachable with the URL and token indicated in the settings. Usesaether.sdk.health.views.check_external
view. For/check-app/app-name
checks if an external application serverAPP_NAME
is reachable with the provided environment variablesAPP_NAME_URL
andAPP_NAME_TOKEN
.Possible responses:
500
-Always Look on the Bright Side of Life!!!
✘200
-Brought to you by eHealth Africa - good tech for hard places
✔
-
the
/check-tokens
URL. Redirects to the user tokens page if any of the external applications is not reachable with the URL indicated in the settings and the linked current user token. Usesaether.sdk.health.views.health
view and theaether.sdk.auth.apptoken.decorators.app_token_required
decorator. -
the
/check-user-tokens
URL (CHECK_TOKEN_URL
setting). Displays the external application tokens for the current user. Usesaether.sdk.auth.apptoken.views.user_app_token_view
view and the templateeha/tokens.html
.
-
-
If
APP_URL
setting is different than/
, then the URL pattern for all endpoints is like:/{app-url}/{endpoint}
. -
If
GATEWAY_ENABLED
isTrue
:The application endpoints are also reachable with a prefixed regular expresion that includes the realm value and the the gateway id for this application.
The URL pattern is like:
/{app-url}/{current-realm}/{gateway-id}/{endpoint}
.The authorization and admin endpoints never depend on any realm so the URLs use always the public realm.
Something like:
/{app-url}/{public-realm}/{gateway-id}/accounts
and/{app-url}/{public-realm}/{gateway-id}/admin
.
The following environment variables are used to build the application django settings. Take a look at the django settings.
Take a look at aether/sdk/conf.settings.py
file to check the list of all
the expected environment variables.
APP_LINK
:https://www.ehealthafrica.org
. The link that appears in the DRF web pages.APP_NAME
:eha
. The application name displayed in the web pages.APP_NAME_HTML
: The HTML expression for the application name. Defaults to the application name.APP_MODULE
: The django module that refers to this application to be included in theINSTALLED_APPS
list.APP_FAVICON
:eha/images/eHA-icon.svg
. The application favicon.APP_LOGO
:eha/images/eHA-icon.svg
. The application logo.APP_URL
:/
. The application URL in the server. If host ishttp://my-server
and the application URL is/my-module
, the application enpoints will be accessible athttp://my-server/my-module/{endpoint}
.
DEBUG
: Enables debug mode. Isfalse
if unset or set to empty string, anything else is consideredtrue
.TESTING
: Indicates if the application executes under test conditions. Isfalse
if unset or set to empty string, anything else is consideredtrue
.LOGGING_FORMATTER
:json
. The application messages format. Possible values:verbose
orjson
.LOGGING_LEVEL
:info
. Logging level for application messages. https://docs.python.org/3/library/logging.html#levelsSENTRY_DSN
: Sentry DSN (error reporting tool). https://docs.sentry.ioPRETTIFIED_CUTOFF
:10000
. Indicates the maximum length of a prettified JSON value. See:aether.sdk.utils.json_prettified(value, indent=2)
method.
DJANGO_SECRET_KEY
: Django secret key for this installation (mandatory). https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-SECRET_KEYLANGUAGE_CODE
:en-us
. Language code for this installation. https://docs.djangoproject.com/en/3.2/ref/settings/#language-codeTIME_ZONE
:UTC
. Time zone for this installation. https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-TIME_ZONESTATIC_URL
:/static/
. Provides a base URL for the static assets to be served from.STATIC_ROOT
:/var/www/static/
. Provides the local folder for the static assets to be served from.
PAGE_SIZE
:10
. Default page size for the REST API.MAX_PAGE_SIZE
:5000
. Maximum page size for the REST API.HTML_SELECT_CUTOFF
:100
. Options size for the REST API Form select fields.
More information in https://docs.djangoproject.com/en/3.2/ref/settings/#databases
PGHOST
: Postgres host name (mandatory).PGPORT
: Postgres port (mandatory).DB_NAME
: Postgres database name (mandatory).PGUSER
: Postgres user (mandatory).PGPASSWORD
: Postgres user password (mandatory).DB_CONN_MAX_AGE
: The lifetime of a database connection, in seconds. Defaults to0
. Value0
means non persistent connections butNone
means persistent.ENABLE_CONNECTION_POOL
: Used to indicate if a connection pooler is enabled. Isfalse
if unset or set to empty string, anything else is consideredtrue
. The expected pooler is pgbouncer that might run as an external service.DB_POOL_INTERNAL
: Used to indicate that an internal connection pooler is used. Isfalse
if unset or set to empty string, anything else is consideredtrue
.SQLAlchemy
library is used to handle internally the connections. Optional environment variables:DB_POOL_INITIAL_SIZE
:20
, the initial number of open connections.DB_POOL_MAX_OVERFLOW
:80
, the number of connections that can be created.DB_POOL_RECYCLE_SECONDS
:3600
(1 hour), the maximum age, in seconds, for a connection before discarding it.DB_POOL_USE_LIFO
: Used to indicate that an internal connection pooler uses LIFO. Isfalse
if unset or set to empty string, anything else is consideredtrue
. Using FIFO vs. LIFO
ADMIN_URL
:admin
. Admin section endpoint.AUTH_URL
:accounts
. Authorization endpoints (login and logout URLs)LOGIN_URL
,/{AUTH_URL}/login
. Login URL.TOKEN_URL
:token
. Get authorization token endpoint.CHECK_TOKEN_URL
:check-user-tokens
. Check authorization tokens endpoint.
LOGIN_TEMPLATE
:eha/login.html
. Template used in the login page.LOGGED_OUT_TEMPLATE
:eha/logged_out.html
. Template used in the logged out page.DRF_API_RENDERER_TEMPLATE
:eha/api.html
. Template used in the DRF browsable API renderer page.DRF_ADMIN_RENDERER_TEMPLATE
:eha/admin.html
. Template used in the DRF API admin renderer page.KEYCLOAK_TEMPLATE
:eha/login_realm.html
. Template used in the login step to get the realm and redirect to keycloak login page.KEYCLOAK_BEHIND_TEMPLATE
:eha/login_keycloak.html
. Template used in the login page when keycloak is enabled behind the scenes.
PROFILING_ENABLED
: Used to indicate if the profiling tool (Silk) is enabled. Isfalse
if unset or set to empty string, anything else is consideredtrue
.SILKY_PYTHON_PROFILER
. Used to indicate if uses Python's built-in cProfile profiler. Isfalse
if unset or set to empty string, anything else is consideredtrue
.SILKY_PYTHON_PROFILER_BINARY
. Used to indicate if generates a binary.prof
file. Isfalse
if unset or set to empty string, anything else is consideredtrue
.SILKY_PYTHON_PROFILER_RESULT_PATH
:/tmp/
. Local directory where the*.prof
files are stored.SILKY_META
. To see what effect Silk is having on the request/response time. Isfalse
if unset or set to empty string, anything else is consideredtrue
.SILKY_MAX_REQUEST_BODY_SIZE
:-1
. Silk saves the request body if its size (in bytes) is less than the indicated value. Any value less than0
means no limit.SILKY_MAX_RESPONSE_BODY_SIZE
:-1
. Silk saves the response body if its size (in bytes) is less than the indicated value. Any value less than0
means no limit.SILKY_INTERCEPT_PERCENT
:100
. Indicates the percentage of requests that are recorded.SILKY_MAX_RECORDED_REQUESTS
:10000
. The number of request/responses stored.SILKY_MAX_RECORDED_REQUESTS_CHECK_PERCENT
:10
.
The /admin/~silk/
URL displays the profiling data (accessible to admin users only).
See more in https://github.com/jazzband/django-silk
STORAGE_REQUIRED
: Used to indicate if the file storage system is required. Isfalse
if unset or set to empty string, anything else is consideredtrue
.DJANGO_STORAGE_BACKEND
: Used to specify a Default file storage system. Available options:minio
,s3
,gcs
.COLLECT_STATIC_FILES_ON_STORAGE
: Used to indicate if static files should be collected on the specified cloud-based storage service (minio
,s3
orgcs
) Isfalse
if unset or set to empty string, anything else is consideredtrue
.CDN_URL
: Used to indicate the public cdn base url to access webpack static files.
More information in https://django-storages.readthedocs.io/en/latest/index.html
BUCKET_NAME
: Name of the bucket that will act as MEDIA folder (mandatory).STATIC_BUCKET_NAME
: Name of the bucket to collect static files (mandatory ifCOLLECT_STATIC_FILES_ON_STORAGE
is set totrue
)MINIO_STORAGE_ACCESS_KEY
: Minio Access Key.MINIO_STORAGE_SECRET_KEY
: Minio Secret Access Key.MINIO_STORAGE_ENDPOINT
: Minio server URL endpoint (without scheme).MINIO_STORAGE_USE_HTTPS
: Whether to use TLS or not. Determines the scheme.MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET
: Whether to create the bucket if it does not already exist.MINIO_STORAGE_MEDIA_USE_PRESIGNED
: Determines if the media file URLs should be pre-signed.
See more in https://django-minio-storage.readthedocs.io/en/latest/usage
BUCKET_NAME
: Name of the bucket to use on s3 (mandatory). Must be unique on s3.STATIC_BUCKET_NAME
: Name of the bucket to collect static files (mandatory ifCOLLECT_STATIC_FILES_ON_STORAGE
is set totrue
)AWS_ACCESS_KEY_ID
: AWS Access Key to your s3 account.AWS_SECRET_ACCESS_KEY
: AWS Secret Access Key to your s3 account.
BUCKET_NAME
: Name of the bucket to use on gcs (mandatory). Create bucket using Google Cloud Console and set appropriate permissions.STATIC_BUCKET_NAME
: Name of the bucket to collect static files (mandatory ifCOLLECT_STATIC_FILES_ON_STORAGE
is set totrue
)
GS_ACCESS_KEY_ID
: Google Cloud Access Key.GS_SECRET_ACCESS_KEY
: Google Cloud Secret Access Key.
How to create Access Keys on Google Cloud Storage
SCHEDULER_REQUIRED
: Used to indicate if the RQ platform is required. Isfalse
if unset or set to empty string, anything else is consideredtrue
.REDIS_HOST
: The redis host name (mandatory).REDIS_PORT
: The redis port (mandatory).REDIS_DB
: The redis database. Defaults to0
.REDIS_PASSWORD
: The redis password.
DJANGO_USE_CACHE
: Used to indicate if the cache is enabled. Isfalse
if unset or set to empty string, anything else is consideredtrue
.DJANGO_CACHE_TIMEOUT
: Cache timeout in seconds. Defaults to300
(5 minutes).REDIS_HOST
: The redis host name (mandatory).REDIS_PORT
: The redis port (mandatory).REDIS_PASSWORD
: The redis password.REDIS_DB_CACHEOPS
: The django ORM Redis database. Defaults to1
.REDIS_DJANGO_CACHE
: Used to indicated if the django cache is handled by Redis. Isfalse
if unset or set to empty string, anything else is consideredtrue
.REDIS_DB_DJANGO
: The django platform Redis database. Defaults to2
.REDIS_SESSION_CACHE
: Used to indicated if the django session cache is handled by Redis. Isfalse
if unset or set to empty string, anything else is consideredtrue
.REDIS_DB_SESSION
: The django session Redis database. Defaults to3
.
See more in django-cacheops
DJANGO_ALLOWED_HOSTS
:*
. SetALLOWED_HOSTS
Django setting. https://docs.djangoproject.com/en/3.2/ref/settings/#allowed-hostsCSRF_COOKIE_DOMAIN
:.ehealthafrica.org
. SetCSRF_COOKIE_DOMAIN
Django setting. https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-cookie-domainCSRF_TRUSTED_ORIGINS
. SetCSRF_TRUSTED_ORIGINS
Django setting. https://docs.djangoproject.com/en/3.2/ref/settings/#csrf-trusted-originsDJANGO_USE_X_FORWARDED_HOST
:False
. SetUSE_X_FORWARDED_HOST
Django setting. https://docs.djangoproject.com/en/3.2/ref/settings/#use-x-forwarded-hostDJANGO_USE_X_FORWARDED_PORT
:False
. SetUSE_X_FORWARDED_PORT
Django setting. https://docs.djangoproject.com/en/3.2/ref/settings/#use-x-forwarded-portDJANGO_HTTP_X_FORWARDED_PROTO
:False
. If present setsSECURE_PROXY_SSL_HEADER
Django setting to('HTTP_X_FORWARDED_PROTO', 'https')
. https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER
WEBPACK_REQUIRED
: Used to indicate if the assets are served via webpack. Isfalse
if unset or set to empty string, anything else is consideredtrue
.WEBPACK_STATS_FILE
:{STATIC_ROOT}webpack-stats.json
, indicates the file path that webpack uses to serve the different assets.
The standard options are to log in via token authentication, via basic authentication or via the standard django authentication.
Set the KEYCLOAK_SERVER_URL
and KEYCLOAK_CLIENT_ID
environment variables if
you want to use Keycloak as authentication server.
KEYCLOAK_CLIENT_ID
(defaults to eha
) is the public client that allows
the aether module to authenticate using the Keycloak REST API.
This client id must be added to all the realms used by the aether module.
The KEYCLOAK_SERVER_URL
must include all the path until the realm is indicated,
usually until /auth/realms
.
There are two ways of setting up keycloak:
a) In this case the authentication process happens in the server side without any further user interaction.
# .env file
KEYCLOAK_SERVER_URL=http://my-keycloak-server/auth/realms
KEYCLOAK_BEHIND_SCENES=true
b) In this case the user is redirected to the keycloak server to finish the sign in step.
# .env file
KEYCLOAK_SERVER_URL=http://my-keycloak-server/auth/realms
KEYCLOAK_BEHIND_SCENES=
Read more in Keycloak.
Note: Multi-tenancy is automatically enabled if the authentication server is keycloak.
Set GATEWAY_SERVICE_ID
to enable gateway authentication with keycloak.
This means that the authentication is handled by a third party system
(like Kong) that includes in each request the JSON Web
Token (JWT) in the GATEWAY_HEADER_TOKEN
header (defaults to X-Oauth-Token
).
The GATEWAY_SERVICE_ID
indicates the gateway service.
In this case the application URLs can be reached in several ways:
Trying to access the health endpoint /health
:
http://my-server/health
using the internal URLhttp://my-gateway-server/my-realm/my-module/health
using the gateway URL (beingmy-module
theGATEWAY_SERVICE_ID
value)
For those endpoints that don't depend on the realm and must also be available "unprotected" we need one more environment variable:
GATEWAY_PUBLIC_REALM
:-
This represents the fake realm that is not protected by the gateway server. In this case the authentication is handled by the other available options, i.e., basic, token...
The authorization and admin endpoints never depend on any realm so the final URLs use always the public realm.
http://my-gateway-server/-/my-module/accounts/
http://my-gateway-server/-/my-module/admin/
The technical implementation is explained in Multi-tenancy README. Follow the instructions to enable multi-tenancy option in your application.
MULTITENANCY
, Enables or disables the feature, isfalse
if unset or set to empty string, anything else is consideredtrue
.DEFAULT_REALM
,eha
The default realm for artefacts created while multi-tenancy was not enabled.REALM_COOKIE
,eha-realm
The name of the cookie that keeps the current tenant id in the request headers.
-
EXTERNAL_APPS
: comma separated list with the external apps that the current instance must be able to connect to and interact with. For each value there should be the correspondent environment variables:<<EXTERNAL_APP>>_URL
: External application server URL (mandatory).<<EXTERNAL_APP>>_TOKEN
: External application authorization token (mandatory).<<EXTERNAL_APP>>_URL_TEST
: External application server URL used in tests. Defaults to the external application server URL.<<EXTERNAL_APP>>_TOKEN_TEST
: External application authorization token used in tests. Defaults to the external application authorization token.
If the EXTERNAL_APPS
equals to app-1,mod-ule-2,pro-d-uct-3
the expected and
mandatory environment variables are:
APP_1_URL
,APP_1_TOKEN
MOD_ULE_2_URL
,MOD_ULE_2_TOKEN
PRO_D_UCT_3_URL
,PRO_D_UCT_3_TOKEN
If the Gateway authentication is enabled instead of using the given token the
application will use the provided GATEWAY_HEADER_TOKEN
value to communicate
with the external application when possible.
# arguments:
# -u | --url required
# -t | --token optional
./manage.py check_url -u=http://my-server/url/to/check
# arguments:
# -u | --username optional (defaults to "admin")
# -p | --password required
# -e | --email optional
# -t | --token optional
./manage.py setup_admin -u=admin -p=password -t=auth_token
# arguments:
# -u | --username required
# -p | --password required
# -e | --realm optional (required if MULTITENANCY enabled)
# -t | --token optional
./manage.py create_user -u=user -p=password -t=auth_token
# arguments:
# -u | --cdn-url required
# -w | --webpack-dir required
# -s | --storage-path optional
./manage.py cdn_publish \
-u=http://cdn-server/path/to/assets \
-w=path/to/assets/bundles \
-s=static/content/by/webpack/