Source code for trustpoint.settings

"""Django settings for trustpoint project.

Generated by 'django-admin startproject' using Django 5.0.1.

For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""

# ruff: noqa: T201  # print is used for DB connection status prior to log availability

import logging
import os
import socket
import time
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
from typing import ClassVar

import django_stubs_ext
import psycopg
from django.utils.translation import gettext_lazy as _

try:
[docs] APP_VERSION = version('trustpoint')
except PackageNotFoundError: APP_VERSION = 'Version not found'
[docs] def app_version(request): return {'APP_VERSION': APP_VERSION}
# Monkeypatching Django, so stubs will work for all generics, # see: https://github.com/typeddjango/django-stubs django_stubs_ext.monkeypatch() # ------------- Paths -------------- # Build paths inside the project like this: BASE_DIR / 'subdir'.
[docs] BASE_DIR = Path(__file__).resolve().parent.parent
[docs] LOCALE_PATHS = [BASE_DIR / Path('trustpoint/locale')]
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/5.0/howto/static-files/
[docs] STATIC_URL = 'static/'
[docs] MEDIA_ROOT = BASE_DIR / Path('media')
[docs] MEDIA_URL = '/media/'
[docs] STATICFILES_DIRS = [BASE_DIR / Path('static')]
[docs] STATIC_ROOT = Path(__file__).parent.parent / Path('collected_static')
[docs] LOG_DIR_PATH = BASE_DIR / Path('media/log/')
LOG_DIR_PATH.mkdir(parents=True, exist_ok=True)
[docs] LOG_FILE_PATH = LOG_DIR_PATH / Path('trustpoint.log')
[docs] BACKUP_FILE_PATH = MEDIA_ROOT / Path('backups')
[docs] PUBLIC_PATHS = [ '/setup-wizard', '/.well-known/cmp', '/.well-known/est', '/aoki', '/crl', ]
# ------------- Functions --------------
[docs] def is_postgre_available() -> bool: """Checks whether PostgreSQL is available and issues differentiated error messages. Returns: bool: True, if PostgreSQL is available and accessible. Raises: RuntimeError: If PostgreSQL is deactivated, not reachable or not accessible. """ if not POSTGRESQL: print('PostgreSQL is disabled. Set POSTGRESQL=True in settings.') return False host = os.environ.get('DATABASE_HOST', DATABASE_HOST) port = int(os.environ.get('DATABASE_PORT', DATABASE_PORT)) user = os.environ.get('DATABASE_USER', DATABASE_USER) password = os.environ.get('DATABASE_PASSWORD', DATABASE_PASSWORD) db_name = os.environ.get('POSTGRES_DB', POSTGRES_DB) try: print(f'Trying to connect to {host}:{port}...') with socket.create_connection((host, port), timeout=5): print(f'Connection to {host}:{port} successful.') except OSError: msg = f'PostgreSQL host {host} on port {port} is unreachable. \n' msg += 'Switching to SQLite Database' print(msg) return False try: print(f"Attempting database login with user '{user}'...") conn = psycopg.connect( dbname=db_name, user=user, password=password, host=host, port=port, ) conn.close() print('Database login successful.') except psycopg.OperationalError as e: msg = f'Failed to log in to PostgreSQL database "{db_name}" as user "{user}". Error: {e}' print(msg) return False return True
# ------------- Variables --------------
[docs] ALLOWED_HOSTS = ['*']
[docs] WSGI_APPLICATION = 'trustpoint.wsgi.application'
# mDNS service discovery advertisement
[docs] ADVERTISED_HOST = '127.0.0.1'
[docs] ADVERTISED_PORT = 443
[docs] DOCKER_CONTAINER = False
# Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # SECURITY WARNING: don't run with debug turned on in production!
[docs] DEBUG = True
[docs] ADMIN_ENABLED = bool(DEBUG)
[docs] DEVELOPMENT_ENV = True
# Settings for postgreql database
[docs] POSTGRESQL = True
[docs] DATABASE_ENGINE = 'django.db.backends.postgresql'
[docs] DATABASE_HOST = 'localhost'
[docs] DATABASE_PORT = '5432'
[docs] POSTGRES_DB = 'trustpoint_db'
[docs] DATABASE_USER = 'admin'
[docs] DATABASE_PASSWORD = 'testing321' # noqa: S105
# Settomg for email backend
[docs] DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'no-reply@trustpoint.de')
# Default: console (safe for dev/showcases) EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' # If EMAIL_HOST is present, switch to SMTP
[docs] _email_host = os.getenv('EMAIL_HOST') # e.g. "smtp.customer.tld" or "mailpit"
if _email_host:
[docs] EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = _email_host EMAIL_PORT = int(os.getenv('EMAIL_PORT', '587')) # Sensible defaults based on port; env can override _use_tls_env = os.getenv('EMAIL_USE_TLS') _use_ssl_env = os.getenv('EMAIL_USE_SSL') EMAIL_USE_TLS = (_use_tls_env.lower() in ('1', 'true', 'yes')) if _use_tls_env else (EMAIL_PORT == 587) EMAIL_USE_SSL = (_use_ssl_env.lower() in ('1', 'true', 'yes')) if _use_ssl_env else (EMAIL_PORT == 465) EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '') # auth only if both non-empty EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') EMAIL_TIMEOUT = int(os.getenv('EMAIL_TIMEOUT', '10'))
[docs] STORAGES = { 'default': { 'BACKEND': 'django.core.files.storage.FileSystemStorage', }, 'staticfiles': { 'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage', }, 'dbbackup': { 'BACKEND': 'django.core.files.storage.FileSystemStorage', 'OPTIONS': { 'location': BACKUP_FILE_PATH, }, }, }
# Default primary key field type # https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
[docs] DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
[docs] CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
[docs] CRISPY_TEMPLATE_PACK = 'bootstrap5'
[docs] LOGIN_REDIRECT_URL = 'home:dashboard'
[docs] LOGIN_URL = 'users:login'
[docs] DJANGO_LOG_LEVEL = 'INFO'
[docs] TAGGIT_CASE_INSENSITIVE = True
[docs] DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
[docs] ROOT_URLCONF = 'trustpoint.urls'
# Internationalization
[docs] LANGUAGE_CODE = 'en-us'
[docs] LANGUAGES = [ ('de', _('German')), ('en', _('English')), ]
[docs] USE_I18N = True
[docs] USE_TZ = True
[docs] TIME_ZONE = 'UTC'
# Application definition
[docs] INSTALLED_APPS = [ 'help_pages.apps.HelpPagesConfig', 'shared.apps.SharedConfig', 'setup_wizard.apps.SetupWizardConfig', 'users.apps.UsersConfig', 'home.apps.HomeConfig', 'devices.apps.DevicesConfig', 'pki.apps.PkiConfig', 'cmp.apps.CmpConfig', 'est.apps.EstConfig', 'aoki.apps.AokiConfig', 'management.apps.ManagementConfig', 'notifications.apps.NotificationsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'crispy_forms', 'crispy_bootstrap5', 'django_filters', 'dbbackup', ]
if DEVELOPMENT_ENV and not DOCKER_CONTAINER: INSTALLED_APPS.append('django_extensions') INSTALLED_APPS.append('behave_django')
[docs] TEST_RUNNER = 'django_behave.runner.DjangoBehaveTestSuiteRunner'
[docs] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'trustpoint.middleware.TrustpointLoginRequiredMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
[docs] TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / Path('templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'trustpoint.settings.app_version', ], }, }, ]
# Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
[docs] AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]
# Database # https://docs.djangoproject.com/en/5.0/ref/settings/#databases if is_postgre_available():
[docs] DATABASES = { 'default': { 'ENGINE': DATABASE_ENGINE, 'NAME': os.environ.get('POSTGRES_DB', POSTGRES_DB), 'USER': os.environ.get('DATABASE_USER', DATABASE_USER), 'PASSWORD': os.environ.get('DATABASE_PASSWORD', DATABASE_PASSWORD), 'HOST': os.environ.get('DATABASE_HOST', DATABASE_HOST), 'PORT': os.environ.get('DATABASE_PORT', DATABASE_PORT), } }
else: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', 'OPTIONS': {'timeout': 20}, }, } # SECURITY WARNING: keep the secret key used in production secret! if DEBUG:
[docs] SECRET_KEY = 'DEV-ENVIRON-SECRET-KEY-lh2rw0b0z$s9e=!4see)@_8ta_up&ad&m01$i+g5z@nz5u$0wi' # noqa: S105
else: # TODO(AlexHx8472): Use proper docker secrets handling. SECRET_KEY = Path('/etc/trustpoint/secrets/django_secret_key.env').read_text()
[docs] class UTCFormatter(logging.Formatter): """Custom logging formatter to use UTC time."""
[docs] converter = time.gmtime
[docs] LOGGING = { 'version': 1, # Indicates the version of the logging configuration 'disable_existing_loggers': False, # Don't disable the default Django logging configuration 'formatters': { 'defaultFormatter': { '()': UTCFormatter, 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s', 'datefmt': DATE_FORMAT, }, }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'defaultFormatter', }, 'rotatingFile': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'defaultFormatter', 'filename': LOG_FILE_PATH, 'maxBytes': 1048576, # 1MB 'backupCount': 7, 'encoding': 'utf8', }, }, 'loggers': { '': { 'level': 'INFO', 'handlers': ['console', 'rotatingFile'], }, }, }
# User interface config defaults
[docs] class UIConfig: """User interface configuration defaults."""
[docs] paginate_by: ClassVar[int] = 50
[docs] notifications_paginate_by: ClassVar[int] = 5