"""Django settings. All deployment values come from environment variables (AD-13).""" import os import sys from pathlib import Path from .env import ConfigError, load_config from .logging import build_logging try: _config = load_config(os.environ) except ConfigError as exc: # One clear message, no traceback, no values. print(str(exc), file=sys.stderr) sys.exit(1) BASE_DIR = Path(__file__).resolve().parent.parent.parent SECRET_KEY = _config.secret_key DEBUG = _config.debug ALLOWED_HOSTS = _config.allowed_hosts # Reserved for the client-IP rule in a later entry; currently has no effect. TRUSTED_PROXIES = _config.trusted_proxies DATA_DIR = _config.data_dir DISPLAY_TIME_ZONE = _config.default_timezone INSTALLED_APPS = [ "django.contrib.contenttypes", "django.contrib.auth", "django.contrib.sessions", "django.contrib.staticfiles", "portal.audit", "portal.accounts", ] MIDDLEWARE = [ "portal.config.middleware.RequestIdMiddleware", "portal.config.middleware.SecurityHeadersMiddleware", "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "portal.accounts.middleware.AbsoluteSessionLimitMiddleware", "django.middleware.locale.LocaleMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "portal.config.urls" WSGI_APPLICATION = "portal.config.wsgi.application" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_DIR / "portal" / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.request", "django.template.context_processors.i18n", ], }, } ] DATABASES = { "default": { "ENGINE": "django.db.backends.sqlite3", "NAME": DATA_DIR / "db.sqlite3", "OPTIONS": { "transaction_mode": "IMMEDIATE", "timeout": 20, "init_command": "PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL;", }, } } DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Time and language (AD-15): store UTC, translate every string. USE_TZ = True TIME_ZONE = "UTC" USE_I18N = True LANGUAGE_CODE = "en" LANGUAGES = [("en", "English"), ("de", "Deutsch")] LOCALE_PATHS = [BASE_DIR / "portal" / "locale"] # Static files served by whitenoise; assets are built into the image. STATIC_URL = "static/" STATIC_ROOT = BASE_DIR / "staticfiles" STATICFILES_DIRS = [BASE_DIR / "portal" / "static"] STORAGES = { "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"}, "staticfiles": {"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage"}, } # Sessions in the database; cookies secure, HttpOnly, SameSite=Lax (AD-6). SESSION_ENGINE = "portal.accounts.session_store" SESSION_COOKIE_AGE = 60 * 60 # idle limit: 1 hour, renewed on every request SESSION_SAVE_EVERY_REQUEST = True SESSION_ABSOLUTE_SECONDS = 4 * 60 * 60 # absolute limit: 4 hours SESSION_COOKIE_SECURE = not DEBUG SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" CSRF_COOKIE_SECURE = not DEBUG CSRF_COOKIE_HTTPONLY = True CSRF_COOKIE_SAMESITE = "Lax" # TLS ends at the reverse proxy. SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_CONTENT_TYPE_NOSNIFF = True SECURE_REFERRER_POLICY = "same-origin" SECURE_HSTS_SECONDS = 0 if DEBUG else 31536000 X_FRAME_OPTIONS = "DENY" LOGGING = build_logging(_config.log_level)