Compare commits

..

No commits in common. "37168500d1064c70415a736c47ce121ea56778b0" and "3d4d34b69d0db829219f2ae5c3c37fb312e4f1a6" have entirely different histories.

15 changed files with 77 additions and 186 deletions

View file

@ -1,23 +1,13 @@
# Django core
DEBUG=True DEBUG=True
SECRET_KEY=dev-secret-key SECRET_KEY=dev-secret-key
DJANGO_SETTINGS_MODULE=config.settings
# Superuser (optional) # Initial User
DJANGO_SUPERUSER_USERNAME=admin DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@example.com DJANGO_SUPERUSER_EMAIL=admin@example.com
DJANGO_SUPERUSER_PASSWORD=changeme DJANGO_SUPERUSER_PASSWORD=changeme
# OIDC / SSO # OIDC
SSO_ENABLED=False SSO_ENABLED=False
OIDC_RP_CLIENT_ID=django-app OIDC_RP_CLIENT_ID=django-app
OIDC_RP_CLIENT_SECRET=changeme OIDC_RP_CLIENT_SECRET=changeme
OIDC_OP_DISCOVERY_ENDPOINT=https://auth.example.com/.well-known/openid-configuration OIDC_OP_DISCOVERY_ENDPOINT=https://auth.example.com/.well-known/openid-configuration
# Database settings (Default: SQLite)
DB_ENGINE=sqlite
DB_NAME=db.sqlite3
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_PORT=

3
.gitignore vendored
View file

@ -1,4 +1 @@
.venv .venv
.startserver.sh
.env
sb.sqlite3

View file

@ -1,20 +1,8 @@
from rest_framework.decorators import api_view, permission_classes from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny
from rest_framework.response import Response from rest_framework.response import Response
@api_view(["GET"]) @api_view(["GET"])
@permission_classes([AllowAny]) # This endpoint is deliberately open to everyone @permission_classes([AllowAny]) # erstmal offen, später absichern
def ping(request): def ping(request):
return Response({"status": "ok"}) return Response({"status": "ok"})
@api_view(["GET"])
@permission_classes([IsAuthenticated]) # Requires either session (OIDC) or basic authentication
def secure_ping(request):
return Response({
"status": "ok",
"user": request.user.username, # The authenticated username
# Indicates whether the request was authenticated via session (OIDC) or via basic auth
"auth_via": request.auth.__class__.__name__ if request.auth else "session/basic"
})

View file

@ -1,47 +1,42 @@
import os
from pathlib import Path
import environ import environ
import os
# --------------------------------------------------------------------------- from pathlib import Path
# Base project paths
# --------------------------------------------------------------------------- # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# --------------------------------------------------------------------------- # Load Env-Vars
# Environment configuration
# ---------------------------------------------------------------------------
env = environ.Env() env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, ".env")) environ.Env.read_env(os.path.join(BASE_DIR, ".env"))
# --------------------------------------------------------------------------- # Quick-start development settings - unsuitable for production
# Security settings # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# ---------------------------------------------------------------------------
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env( SECRET_KEY = env(
"SECRET_KEY", "SECRET_KEY",
default="django-insecure-lbfv*h@=mjj#xq^!k@-5f2oiq@u6ms9t6=3&nr+!#itih%jh^l" # fallback only for development default="django-insecure-lbfv*h@=mjj#xq^!k@-5f2oiq@u6ms9t6=3&nr+!#itih%jh^l"
) )
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
# --------------------------------------------------------------------------- # SECURITY WARNING: don't run with debug turned on in production!
# Installed apps DEBUG = env.bool("DEBUG", default=True)
# ---------------------------------------------------------------------------
ALLOWED_HOSTS = ["localhost","127.0.0.1"]
# Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
# Django built-in apps
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
"django.contrib.sessions", "django.contrib.sessions",
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
# Third-party apps
"rest_framework", "rest_framework",
] ]
# ---------------------------------------------------------------------------
# Middleware
# ---------------------------------------------------------------------------
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
@ -52,19 +47,12 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
# ---------------------------------------------------------------------------
# URL & WSGI configuration
# ---------------------------------------------------------------------------
ROOT_URLCONF = "config.urls" ROOT_URLCONF = "config.urls"
WSGI_APPLICATION = "config.wsgi.application"
# ---------------------------------------------------------------------------
# Templates
# ---------------------------------------------------------------------------
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [], # can be extended if custom templates are needed "DIRS": [],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"context_processors": [ "context_processors": [
@ -77,100 +65,89 @@ TEMPLATES = [
}, },
] ]
# --------------------------------------------------------------------------- WSGI_APPLICATION = "config.wsgi.application"
# Database configuration
# ---------------------------------------------------------------------------
DB_ENGINE = env("DB_ENGINE", default="sqlite").lower()
if DB_ENGINE == "postgres":
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": env("DB_NAME", default="postgres"),
"USER": env("DB_USER", default="postgres"),
"PASSWORD": env("DB_PASSWORD", default=""),
"HOST": env("DB_HOST", default="localhost"),
"PORT": env("DB_PORT", default="5432"),
}
}
elif DB_ENGINE == "mysql": # Database
DATABASES = { # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": env("DB_NAME", default="mysql"),
"USER": env("DB_USER", default="root"),
"PASSWORD": env("DB_PASSWORD", default=""),
"HOST": env("DB_HOST", default="localhost"),
"PORT": env("DB_PORT", default="3306"),
"OPTIONS": {
"charset": "utf8mb4", # recommended for full Unicode support
},
}
}
else: # default: SQLite DATABASES = {
DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.sqlite3", "ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3", # fixed filename for simplicity "NAME": BASE_DIR / "db.sqlite3",
}
} }
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
# ---------------------------------------------------------------------------
# Authentication & password validation
# ---------------------------------------------------------------------------
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"}, {
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, },
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, {
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
] ]
# ---------------------------------------------------------------------------
# Internationalization # Internationalization
# --------------------------------------------------------------------------- # https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = "en-us" LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC" TIME_ZONE = "UTC"
USE_I18N = True USE_I18N = True
USE_TZ = True USE_TZ = True
# ---------------------------------------------------------------------------
# Static files # Static files (CSS, JavaScript, Images)
# --------------------------------------------------------------------------- # https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
# ---------------------------------------------------------------------------
# Default primary key field type # Default primary key field type
# --------------------------------------------------------------------------- # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# ---------------------------------------------------------------------------
# Django REST framework configuration
# ---------------------------------------------------------------------------
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": [ "DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated", # all endpoints protected by default "rest_framework.permissions.IsAuthenticated",
], ],
"DEFAULT_AUTHENTICATION_CLASSES": [ "DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.SessionAuthentication", # required for OIDC/session login "rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication", # allows Basic Auth for API clients "rest_framework.authentication.BasicAuthentication",
], ],
} }
# --------------------------------------------------------------------------- # OIDC Vars
# OpenID Connect (SSO) configuration
# ---------------------------------------------------------------------------
SSO_ENABLED = env.bool("SSO_ENABLED", default=False) SSO_ENABLED = env.bool("SSO_ENABLED", default=False)
if SSO_ENABLED: if SSO_ENABLED:
INSTALLED_APPS += ["mozilla_django_oidc"] INSTALLED_APPS += [
MIDDLEWARE += ["mozilla_django_oidc.middleware.SessionRefresh"] "mozilla_django_oidc",
]
MIDDLEWARE += [
"mozilla_django_oidc.middleware.SessionRefresh",
]
LOGIN_URL = "/oidc/authenticate/" LOGIN_URL = "/oidc/authenticate/"
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/" LOGOUT_REDIRECT_URL = "/"
OIDC_RP_CLIENT_ID = env("OIDC_RP_CLIENT_ID", default="django-app") OIDC_RP_CLIENT_ID = env("OIDC_RP_CLIENT_ID", default="django-app")
OIDC_RP_CLIENT_SECRET = env("OIDC_RP_CLIENT_SECRET", default="changeme") OIDC_RP_CLIENT_SECRET = env("OIDC_RP_CLIENT_SECRET", default="changeme")
OIDC_OP_DISCOVERY_ENDPOINT = env( OIDC_OP_DISCOVERY_ENDPOINT = env(

View file

@ -1,16 +1,13 @@
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from api.views import ping, secure_ping from api.views import ping
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("api/ping/", ping), # Public healthcheck endpoint path("api/ping/", ping),
path("api/secure-ping/", secure_ping) # Protected API endpoint
] ]
# Add OIDC routes only if Single Sign-On is enabled
if settings.SSO_ENABLED: if settings.SSO_ENABLED:
urlpatterns += [ urlpatterns += [
path("oidc/", include("mozilla_django_oidc.urls")), path("oidc/", include("mozilla_django_oidc.urls")),

Binary file not shown.

View file

@ -1,9 +0,0 @@
---
services:
risk-management:
build: .
container_name: django_web
ports:
- "8000:8000"
volumes:
- ./data/app/:/app

View file

@ -1,8 +1,6 @@
#!/bin/sh #!/bin/sh
set -e set -e
export DJANGO_SETTINGS_MODULE=config.settings
echo "Running migrations..." echo "Running migrations..."
python manage.py migrate --noinput python manage.py migrate --noinput

View file

@ -1,47 +0,0 @@
#!/usr/bin/env bash
# -----------------------------------------------------------------------------
# Startup script for the Django project
# -----------------------------------------------------------------------------
# This script ensures that the development environment is ready to run.
#
# Features:
# - Runs database migrations
# - Creates a superuser if it does not already exist
# - Starts the Django development server
#
# Note:
# This script is for local development only.
# In production you should use a proper WSGI/ASGI server (e.g., Gunicorn, Daphne).
# -----------------------------------------------------------------------------
set -e # Exit immediately if a command exits with a non-zero status
# Load environment variables from .env if available
if [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs)
fi
# Ensure we are in the project root directory
cd "$(dirname "$0")"
echo ">>> Running migrations..."
python manage.py migrate --noinput
echo ">>> Checking if superuser exists..."
python manage.py shell <<EOF
from django.contrib.auth import get_user_model
User = get_user_model()
username = "${DJANGO_SUPERUSER_USERNAME:-admin}"
if not User.objects.filter(username=username).exists():
print(f"Creating superuser '{username}'...")
User.objects.create_superuser(
username=username,
email="${DJANGO_SUPERUSER_EMAIL:-admin@example.com}",
password="${DJANGO_SUPERUSER_PASSWORD:-changeme}"
)
else:
print(f"Superuser '{username}' already exists.")
EOF
echo ">>> Starting Django development server at http://127.0.0.1:8000 ..."
python manage.py runserver 0.0.0.0:8000