From e1a009047c777c09318405b7fc972f576c86a4aa Mon Sep 17 00:00:00 2001 From: gabdevele Date: Wed, 17 Jun 2026 18:08:25 +0200 Subject: [PATCH] feat: refractor CIE auth stack configuration for improved environment variable management and docker compatibility --- .env.example | 8 ++ README.md | 110 +++++++----------- docker-compose.yml | 40 ++++++- entrypoint.sh | 2 +- .../federation_authority/settingslocal.py | 16 ++- provider/provider/settingslocal.py | 23 ++-- relying_party/relying_party/settingslocal.py | 32 +++-- 7 files changed, 136 insertions(+), 95 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b74b637 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +CIE_AUTH_ALLOWED_HOSTS=cie-auth.example.it,auth.example.it +DJANGO_TRUST_ANCHOR=https://federation-authority.example.it +DJANGO_PROVIDER_ISSUER=https://provider.example.it/oidc/op +DJANGO_RP_BACKEND_EXCHANGE_URL=https://visione-backend.example.it/api/v1/auth/cie/exchange +DJANGO_RP_SHARED_SECRET=change-me +FEDERATION_AUTHORITY_SECRET_KEY=change-me +PROVIDER_SECRET_KEY=change-me +RELYING_PARTY_SECRET_KEY=change-me diff --git a/README.md b/README.md index e58f21e..52fa223 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,58 @@ -# Visione Local CIE Stack +# Visione CIE Auth Stack -This folder is a ready-to-run local stack for CIE auth integration with Visione. - -Included services: +This folder contains deployable CIE auth services for Visione: - `federation-authority` - `provider` - `relying-party` -They run through one compose file and use the modified relying party bridge for Visione. +It is now structured to work with Docker Compose and Dokploy without host networking. -## Prerequisites -- Visione backend running on `http://127.0.0.1:5555` -- Docker with Compose +## Configuration +Copy [.env.example](/workspace/cie-auth/.env.example) to `.env` and set only what changes in real deploy: +- public domains / internal URLs +- Django secret keys +- shared secret between relying party and Visione backend -## Visione config -Set in `config.json`: +Main variables: +- `CIE_AUTH_ALLOWED_HOSTS` +- `DJANGO_TRUST_ANCHOR` +- `DJANGO_PROVIDER_ISSUER` +- `DJANGO_RP_BACKEND_EXCHANGE_URL` +- `DJANGO_RP_SHARED_SECRET` +- `FEDERATION_AUTHORITY_SECRET_KEY` +- `PROVIDER_SECRET_KEY` +- `RELYING_PARTY_SECRET_KEY` -```json -"cie_auth": { - "service_base_url": "http://127.0.0.1:8001", - "start_path": "/visione/start", - "shared_secret": "change-me-for-local-dev", - "state_ttl_minutes": 10, - "default_frontend_redirect_uri": "http://localhost:3000/auth/cie/callback" -} -``` +## Deploy with Dokploy +Use [docker-compose.yml](/workspace/cie-auth/docker-compose.yml) as compose source. +Expose: +- `federation-authority` on port `8000` +- `provider` on port `8002` +- `relying-party` on port `8001` -Keep same secret also in: -- `relying_party/relying_party/settingslocal.py` - `VISIONE_SHARED_SECRET` +Each service persists its own SQLite DB in a named volume. -## Start stack -From repo root: +## Local start ```bash -docker compose up -d -docker compose -f infra/cie-auth/docker-compose.yml up --build -d +docker compose up --build -d ``` -This starts: -- MySQL + Mailpit from root compose -- authority on `127.0.0.1:8000` -- provider on `127.0.0.1:8002` -- relying party on `127.0.0.1:8001` +## Visione backend +Visione backend must expose exchange endpoint configured in `DJANGO_RP_BACKEND_EXCHANGE_URL`. +Shared secret must match backend `cie_auth.shared_secret`. -## Start Visione -From repo root: +## Bootstrap behavior +On first boot, each Django service: +1. runs migrations +2. loads `dumps/example.json` only if DB is empty -```bash -cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -cmake --build build -./build/visione-backend -``` +Disable fixture import per service with: +- `FEDERATION_AUTHORITY_LOAD_FIXTURE_ON_EMPTY_DB=false` +- `PROVIDER_LOAD_FIXTURE_ON_EMPTY_DB=false` +- `RELYING_PARTY_LOAD_FIXTURE_ON_EMPTY_DB=false` ## Smoke test -Start login: ```bash curl -sS -X POST http://127.0.0.1:5555/api/v1/auth/cie/start \ @@ -62,45 +60,19 @@ curl -sS -X POST http://127.0.0.1:5555/api/v1/auth/cie/start \ -d '{"frontend_redirect_uri":"http://localhost:3000/auth/cie/callback"}' ``` -Expected JSON: -- `authorization_url` -- `state` - -Then: -1. open `authorization_url` in browser -2. complete demo CIE login -3. browser lands on `http://localhost:3000/auth/cie/callback?state=...` - -Poll result: +Then complete login and poll: ```bash curl -sS http://127.0.0.1:5555/api/v1/auth/cie/session/ ``` -Expected success: -- `status = "authenticated"` -- `auth.token` present - -## Frontend flow -1. call `POST /api/v1/auth/cie/start` -2. redirect browser to `authorization_url` -3. callback page reads `state` -4. poll `GET /api/v1/auth/cie/session/{state}` -5. when authenticated, save `auth.token` -6. call `GET /api/v1/me` - -## Reset local auth DBs -If you want a clean auth stack: +## Reset data ```bash -rm -f infra/cie-auth/federation_authority/db.sqlite3 -rm -f infra/cie-auth/provider/db.sqlite3 -rm -f infra/cie-auth/relying_party/db.sqlite3 -docker compose -f infra/cie-auth/docker-compose.yml down -docker compose -f infra/cie-auth/docker-compose.yml up --build -d +docker compose down -v ``` ## Logs ```bash -docker compose -f infra/cie-auth/docker-compose.yml logs -f +docker compose logs -f ``` diff --git a/docker-compose.yml b/docker-compose.yml index 9dd1891..837fc7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,33 +3,67 @@ services: build: context: . dockerfile: Dockerfile - network_mode: host working_dir: /app/federation_authority command: ["/app/federation_authority", "8000"] + environment: + DJANGO_SECRET_KEY: ${FEDERATION_AUTHORITY_SECRET_KEY:-visione-cie-authority-local-dev} + DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*} + DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000} + LOAD_FIXTURE_ON_EMPTY_DB: ${FEDERATION_AUTHORITY_LOAD_FIXTURE_ON_EMPTY_DB:-true} + ports: + - "${FEDERATION_AUTHORITY_PORT:-8000}:8000" volumes: - ./federation_authority:/app/federation_authority + - federation-authority-data:/app/federation_authority/db + restart: unless-stopped provider: build: context: . dockerfile: Dockerfile - network_mode: host working_dir: /app/provider command: ["/app/provider", "8002"] + environment: + DJANGO_SECRET_KEY: ${PROVIDER_SECRET_KEY:-visione-cie-provider-local-dev} + DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*} + DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000} + DJANGO_PROVIDER_ISSUER: ${DJANGO_PROVIDER_ISSUER:-http://provider:8002/oidc/op} + LOAD_FIXTURE_ON_EMPTY_DB: ${PROVIDER_LOAD_FIXTURE_ON_EMPTY_DB:-true} + ports: + - "${PROVIDER_PORT:-8002}:8002" volumes: - ./provider:/app/provider + - provider-data:/app/provider/db depends_on: - federation-authority + restart: unless-stopped relying-party: build: context: . dockerfile: Dockerfile - network_mode: host working_dir: /app/relying_party command: ["/app/relying_party", "8001"] + environment: + DJANGO_SECRET_KEY: ${RELYING_PARTY_SECRET_KEY:-visione-cie-rp-local-dev} + DJANGO_ALLOWED_HOSTS: ${CIE_AUTH_ALLOWED_HOSTS:-*} + DJANGO_TRUST_ANCHOR: ${DJANGO_TRUST_ANCHOR:-http://federation-authority:8000} + DJANGO_PROVIDER_ISSUER: ${DJANGO_PROVIDER_ISSUER:-http://provider:8002/oidc/op} + DJANGO_RP_BACKEND_EXCHANGE_URL: ${DJANGO_RP_BACKEND_EXCHANGE_URL:-http://visione-backend:5555/api/v1/auth/cie/exchange} + DJANGO_RP_SHARED_SECRET: ${DJANGO_RP_SHARED_SECRET:-change-me-for-local-dev} + DJANGO_RP_DEFAULT_PROVIDER: ${DJANGO_RP_DEFAULT_PROVIDER:-http://provider:8002/oidc/op} + LOAD_FIXTURE_ON_EMPTY_DB: ${RELYING_PARTY_LOAD_FIXTURE_ON_EMPTY_DB:-true} + ports: + - "${RELYING_PARTY_PORT:-8001}:8001" volumes: - ./relying_party:/app/relying_party + - relying-party-data:/app/relying_party/db depends_on: - federation-authority - provider + restart: unless-stopped + +volumes: + federation-authority-data: + provider-data: + relying-party-data: diff --git a/entrypoint.sh b/entrypoint.sh index ef5816a..f80eb55 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -8,7 +8,7 @@ cd "$APP_DIR" mkdir -p logs static -if [ ! -f db.sqlite3 ]; then +if [ ! -f "$APP_DIR/db.sqlite3" ] && [ "${LOAD_FIXTURE_ON_EMPTY_DB:-true}" = "true" ]; then python manage.py migrate python manage.py loaddata dumps/example.json else diff --git a/federation_authority/federation_authority/settingslocal.py b/federation_authority/federation_authority/settingslocal.py index e1bb5c6..bd0e1b8 100644 --- a/federation_authority/federation_authority/settingslocal.py +++ b/federation_authority/federation_authority/settingslocal.py @@ -3,12 +3,18 @@ import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -SECRET_KEY = 'visione-cie-authority-local-dev' -DEBUG = True +def env_bool(name, default=False): + value = os.getenv(name) + if value is None: + return default + return value.lower() in {"1", "true", "yes", "on"} + +SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-authority-local-dev') +DEBUG = env_bool('DJANGO_DEBUG', False) ADMIN_PATH = 'admin/' APPEND_SLASH = False -OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000" +OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000") OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR] OIDCFED_PROVIDER_PROFILE = "spid" OIDCFED_PROVIDER_MAX_REFRESH = 10 @@ -28,7 +34,7 @@ LOGIN_REDIRECT_URL = "/oidc/rp/echo_attributes" LOGOUT_REDIRECT_URL = "/oidc/rp/landing" LOGIN_URL = "/oidc/rp/landing" -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()] AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User' INSTALLED_APPS = [ @@ -85,7 +91,7 @@ LOGGING = { 'handlers': { 'console': { 'formatter': 'default', - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.StreamHandler', }, }, diff --git a/provider/provider/settingslocal.py b/provider/provider/settingslocal.py index fd0ce96..7600bf1 100644 --- a/provider/provider/settingslocal.py +++ b/provider/provider/settingslocal.py @@ -3,23 +3,32 @@ import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -SECRET_KEY = 'visione-cie-provider-local-dev' -DEBUG = True +def env_bool(name, default=False): + value = os.getenv(name) + if value is None: + return default + return value.lower() in {"1", "true", "yes", "on"} + + +SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-provider-local-dev') +DEBUG = env_bool('DJANGO_DEBUG', False) ADMIN_PATH = 'admin/' APPEND_SLASH = False -OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000" +OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000") OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR] OIDCFED_PROVIDER_PROFILE = "cie" OIDCFED_PROVIDER_MAX_CONSENT_TIMEFRAME = 3600 OIDCFED_REQUIRED_TRUST_MARKS = [] +provider_issuer = os.getenv("DJANGO_PROVIDER_ISSUER", "http://127.0.0.1:8002/oidc/op") + OIDCFED_IDENTITY_PROVIDERS = { "spid": { - "http://127.0.0.1:8000/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, + f"{OIDCFED_DEFAULT_TRUST_ANCHOR}/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, }, "cie": { - "http://127.0.0.1:8002/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, + provider_issuer: OIDCFED_DEFAULT_TRUST_ANCHOR, }, } @@ -32,7 +41,7 @@ LOGIN_REDIRECT_URL = "/oidc/op/landing" LOGOUT_REDIRECT_URL = "/oidc/op/landing" LOGIN_URL = "/oidc/op/landing" -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()] AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User' INSTALLED_APPS = [ @@ -86,7 +95,7 @@ LOGGING = { 'handlers': { 'console': { 'formatter': 'default', - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.StreamHandler', }, }, diff --git a/relying_party/relying_party/settingslocal.py b/relying_party/relying_party/settingslocal.py index a5227f7..9b36207 100644 --- a/relying_party/relying_party/settingslocal.py +++ b/relying_party/relying_party/settingslocal.py @@ -3,21 +3,30 @@ import os BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -SECRET_KEY = 'visione-cie-rp-local-dev' -DEBUG = True +def env_bool(name, default=False): + value = os.getenv(name) + if value is None: + return default + return value.lower() in {"1", "true", "yes", "on"} + + +SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', 'visione-cie-rp-local-dev') +DEBUG = env_bool('DJANGO_DEBUG', False) ADMIN_PATH = 'admin/' APPEND_SLASH = False -OIDCFED_DEFAULT_TRUST_ANCHOR = "http://127.0.0.1:8000" +OIDCFED_DEFAULT_TRUST_ANCHOR = os.getenv("DJANGO_TRUST_ANCHOR", "http://127.0.0.1:8000") OIDCFED_TRUST_ANCHORS = [OIDCFED_DEFAULT_TRUST_ANCHOR] OIDCFED_REQUIRED_TRUST_MARKS = [] +provider_issuer = os.getenv("DJANGO_PROVIDER_ISSUER", "http://127.0.0.1:8002/oidc/op") + OIDCFED_IDENTITY_PROVIDERS = { "spid": { - "http://127.0.0.1:8000/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, + f"{OIDCFED_DEFAULT_TRUST_ANCHOR}/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, }, "cie": { - "http://127.0.0.1:8002/oidc/op": OIDCFED_DEFAULT_TRUST_ANCHOR, + provider_issuer: OIDCFED_DEFAULT_TRUST_ANCHOR, }, } @@ -30,12 +39,15 @@ LOGIN_REDIRECT_URL = "/visione/complete" LOGOUT_REDIRECT_URL = "/oidc/rp/landing" LOGIN_URL = "/oidc/rp/landing" -VISIONE_BACKEND_EXCHANGE_URL = "http://127.0.0.1:5555/api/v1/auth/cie/exchange" -VISIONE_SHARED_SECRET = "change-me-for-local-dev" -VISIONE_DEFAULT_PROVIDER = "http://127.0.0.1:8002/oidc/op" +VISIONE_BACKEND_EXCHANGE_URL = os.getenv( + "DJANGO_RP_BACKEND_EXCHANGE_URL", + "http://127.0.0.1:5555/api/v1/auth/cie/exchange" +) +VISIONE_SHARED_SECRET = os.getenv("DJANGO_RP_SHARED_SECRET", "change-me-for-local-dev") +VISIONE_DEFAULT_PROVIDER = os.getenv("DJANGO_RP_DEFAULT_PROVIDER", provider_issuer) VISIONE_DEFAULT_PROFILE = "cie" -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = [item.strip() for item in os.getenv('DJANGO_ALLOWED_HOSTS', '*').split(',') if item.strip()] AUTH_USER_MODEL = 'spid_cie_oidc_accounts.User' INSTALLED_APPS = [ @@ -89,7 +101,7 @@ LOGGING = { 'handlers': { 'console': { 'formatter': 'default', - 'level': 'DEBUG', + 'level': 'INFO', 'class': 'logging.StreamHandler', }, },