99 lines
3.9 KiB
Python
99 lines
3.9 KiB
Python
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import secrets
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
|
|||
|
|
BASE_DIR = Path(__file__).resolve().parents[1]
|
|||
|
|
DATA_DIR = Path(os.getenv("CODEXPI_DATA_DIR", BASE_DIR / "data"))
|
|||
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def env_bool(name: str, default: bool) -> bool:
|
|||
|
|
value = os.getenv(name)
|
|||
|
|
return default if value is None else value.lower() in {"1", "true", "yes", "on"}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def secret_key() -> str:
|
|||
|
|
configured_file = os.getenv("CODEXPI_DJANGO_SECRET_KEY_FILE")
|
|||
|
|
if configured_file:
|
|||
|
|
value = Path(configured_file).read_text(encoding="utf-8").strip()
|
|||
|
|
if len(value) < 32:
|
|||
|
|
raise RuntimeError("CODEXPI_DJANGO_SECRET_KEY_FILE doit contenir un secret d’au moins 32 caractères")
|
|||
|
|
return value
|
|||
|
|
configured = os.getenv("CODEXPI_DJANGO_SECRET_KEY")
|
|||
|
|
if configured:
|
|||
|
|
if len(configured) < 32:
|
|||
|
|
raise RuntimeError("CODEXPI_DJANGO_SECRET_KEY doit contenir au moins 32 caractères")
|
|||
|
|
return configured
|
|||
|
|
path = DATA_DIR / "django_secret_key"
|
|||
|
|
if not path.exists():
|
|||
|
|
try:
|
|||
|
|
descriptor = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
|||
|
|
with os.fdopen(descriptor, "w", encoding="utf-8") as target:
|
|||
|
|
target.write(secrets.token_urlsafe(64))
|
|||
|
|
except FileExistsError:
|
|||
|
|
pass
|
|||
|
|
return path.read_text(encoding="utf-8").strip()
|
|||
|
|
|
|||
|
|
|
|||
|
|
SECRET_KEY = secret_key()
|
|||
|
|
DEBUG = False
|
|||
|
|
ALLOWED_HOSTS = [item.strip() for item in os.getenv("CODEXPI_DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1,.ts.net,nginx").split(",") if item.strip()]
|
|||
|
|
CSRF_TRUSTED_ORIGINS = [item.strip() for item in os.getenv("CODEXPI_TRUSTED_ORIGINS", "https://*.ts.net").split(",") if item.strip()]
|
|||
|
|
|
|||
|
|
INSTALLED_APPS = [
|
|||
|
|
"django.contrib.auth",
|
|||
|
|
"django.contrib.contenttypes",
|
|||
|
|
"django.contrib.sessions",
|
|||
|
|
"django_ui.identity",
|
|||
|
|
]
|
|||
|
|
MIDDLEWARE = [
|
|||
|
|
"django.middleware.security.SecurityMiddleware",
|
|||
|
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|||
|
|
"django.middleware.common.CommonMiddleware",
|
|||
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|||
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|||
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|||
|
|
]
|
|||
|
|
ROOT_URLCONF = "django_ui.urls"
|
|||
|
|
ASGI_APPLICATION = "django_ui.asgi.application"
|
|||
|
|
WSGI_APPLICATION = "django_ui.wsgi.application"
|
|||
|
|
|
|||
|
|
DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": DATA_DIR / "django.sqlite3", "OPTIONS": {"timeout": 30}}}
|
|||
|
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|||
|
|
USE_TZ = True
|
|||
|
|
TIME_ZONE = "Europe/Paris"
|
|||
|
|
|
|||
|
|
AUTH_PASSWORD_VALIDATORS = [
|
|||
|
|
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
|||
|
|
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", "OPTIONS": {"min_length": 12}},
|
|||
|
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|||
|
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
|||
|
|
]
|
|||
|
|
PASSWORD_HASHERS = [
|
|||
|
|
"django.contrib.auth.hashers.Argon2PasswordHasher",
|
|||
|
|
"django.contrib.auth.hashers.PBKDF2PasswordHasher",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
SESSION_ENGINE = "django.contrib.sessions.backends.db"
|
|||
|
|
SESSION_COOKIE_NAME = os.getenv("CODEXPI_COOKIE_NAME", "codexpi_session")
|
|||
|
|
SESSION_COOKIE_AGE = int(os.getenv("CODEXPI_SESSION_HOURS", "12")) * 3600
|
|||
|
|
SESSION_COOKIE_SECURE = env_bool("CODEXPI_COOKIE_SECURE", True)
|
|||
|
|
SESSION_COOKIE_HTTPONLY = True
|
|||
|
|
SESSION_COOKIE_SAMESITE = "Strict"
|
|||
|
|
SESSION_SAVE_EVERY_REQUEST = False
|
|||
|
|
CSRF_COOKIE_SECURE = SESSION_COOKIE_SECURE
|
|||
|
|
CSRF_COOKIE_HTTPONLY = True
|
|||
|
|
CSRF_COOKIE_SAMESITE = "Strict"
|
|||
|
|
CSRF_HEADER_NAME = "HTTP_X_CSRF_TOKEN"
|
|||
|
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|||
|
|
USE_X_FORWARDED_HOST = True
|
|||
|
|
SECURE_CONTENT_TYPE_NOSNIFF = True
|
|||
|
|
SECURE_REFERRER_POLICY = "no-referrer"
|
|||
|
|
SECURE_HSTS_SECONDS = 31_536_000
|
|||
|
|
X_FRAME_OPTIONS = "DENY"
|
|||
|
|
|
|||
|
|
CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": "codexpi-login-limiter"}}
|