feat: add federation authority, provider, and relying party directories to Dockerfile; enhance entrypoint script for database normalization
This commit is contained in:
@@ -18,6 +18,10 @@ COPY vendor/spid-cie-oidc-django/setup.py /opt/spid_cie_oidc/setup.py
|
|||||||
COPY vendor/spid-cie-oidc-django/README.md /opt/spid_cie_oidc/README.md
|
COPY vendor/spid-cie-oidc-django/README.md /opt/spid_cie_oidc/README.md
|
||||||
RUN pip install --no-cache-dir /opt/spid_cie_oidc
|
RUN pip install --no-cache-dir /opt/spid_cie_oidc
|
||||||
|
|
||||||
|
COPY federation_authority /app/federation_authority
|
||||||
|
COPY provider /app/provider
|
||||||
|
COPY relying_party /app/relying_party
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
COPY scripts /app/scripts
|
COPY scripts /app/scripts
|
||||||
RUN chmod +x /entrypoint.sh
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "8000"
|
- "8000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./federation_authority:/app/federation_authority
|
|
||||||
- federation-authority-data:/app/federation_authority/db
|
- federation-authority-data:/app/federation_authority/db
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
@@ -34,7 +33,6 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "8002"
|
- "8002"
|
||||||
volumes:
|
volumes:
|
||||||
- ./provider:/app/provider
|
|
||||||
- provider-data:/app/provider/db
|
- provider-data:/app/provider/db
|
||||||
depends_on:
|
depends_on:
|
||||||
- federation-authority
|
- federation-authority
|
||||||
@@ -60,7 +58,6 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "8001"
|
- "8001"
|
||||||
volumes:
|
volumes:
|
||||||
- ./relying_party:/app/relying_party
|
|
||||||
- relying-party-data:/app/relying_party/db
|
- relying-party-data:/app/relying_party/db
|
||||||
depends_on:
|
depends_on:
|
||||||
- federation-authority
|
- federation-authority
|
||||||
|
|||||||
+18
-5
@@ -4,26 +4,39 @@ set -eu
|
|||||||
APP_DIR="$1"
|
APP_DIR="$1"
|
||||||
PORT="$2"
|
PORT="$2"
|
||||||
DB_PATH="${DJANGO_DB_PATH:-$APP_DIR/db/db.sqlite3}"
|
DB_PATH="${DJANGO_DB_PATH:-$APP_DIR/db/db.sqlite3}"
|
||||||
|
DUMP_SRC="$APP_DIR/dumps/example.json"
|
||||||
|
LEGACY_DB_PATH="$APP_DIR/db.sqlite3"
|
||||||
|
|
||||||
cd "$APP_DIR"
|
cd "$APP_DIR"
|
||||||
|
|
||||||
mkdir -p logs static "$(dirname "$DB_PATH")"
|
mkdir -p logs static "$(dirname "$DB_PATH")"
|
||||||
|
|
||||||
|
if [ ! -f "$DB_PATH" ] && [ -f "$LEGACY_DB_PATH" ]; then
|
||||||
|
mkdir -p "$(dirname "$DB_PATH")"
|
||||||
|
cp "$LEGACY_DB_PATH" "$DB_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "$DB_PATH" ] && [ "${LOAD_FIXTURE_ON_EMPTY_DB:-true}" = "true" ]; then
|
if [ ! -f "$DB_PATH" ] && [ "${LOAD_FIXTURE_ON_EMPTY_DB:-true}" = "true" ]; then
|
||||||
TMP_DUMP="$(mktemp)"
|
|
||||||
python /app/scripts/rewrite_dumps.py dumps/example.json "$TMP_DUMP"
|
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py loaddata "$TMP_DUMP"
|
if [ -f "$DUMP_SRC" ]; then
|
||||||
rm -f "$TMP_DUMP"
|
TMP_DUMP="$(mktemp)"
|
||||||
|
python /app/scripts/rewrite_dumps.py "$DUMP_SRC" "$TMP_DUMP"
|
||||||
|
python manage.py loaddata "$TMP_DUMP"
|
||||||
|
rm -f "$TMP_DUMP"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
python /app/scripts/normalize_db_urls.py "$DB_PATH"
|
||||||
|
|
||||||
# With DEBUG disabled Django no longer serves app static assets implicitly.
|
# With DEBUG disabled Django no longer serves app static assets implicitly.
|
||||||
# Collect them into STATIC_ROOT so the explicit /static/ URL can serve them.
|
# Collect them into STATIC_ROOT so the explicit /static/ URL can serve them.
|
||||||
python manage.py collectstatic --noinput
|
python manage.py collectstatic --noinput
|
||||||
|
|
||||||
if grep -aEq '127\.0\.0\.1|provider:8002' "$DB_PATH"; then
|
if grep -aEq '127\.0\.0\.1|provider:8002' "$DB_PATH" \
|
||||||
|
&& [ -n "${DJANGO_ALLOWED_HOSTS:-}" ] \
|
||||||
|
&& [ "${DJANGO_ALLOWED_HOSTS:-*}" != "*" ]; then
|
||||||
echo "Refusing to start: bootstrap data still contains internal/local URLs" >&2
|
echo "Refusing to start: bootstrap data still contains internal/local URLs" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
|
def env(name: str, default: str = "") -> str:
|
||||||
|
return os.getenv(name, default).strip().rstrip("/")
|
||||||
|
|
||||||
|
|
||||||
|
def infer_base_url(kind: str) -> str:
|
||||||
|
hosts = env("DJANGO_ALLOWED_HOSTS") or env("CIE_AUTH_ALLOWED_HOSTS")
|
||||||
|
items = [host.strip() for host in hosts.split(",") if host.strip() and host.strip() != "*"]
|
||||||
|
for host in items:
|
||||||
|
if kind in host:
|
||||||
|
return f"https://{host}"
|
||||||
|
defaults = {
|
||||||
|
"fa": "http://federation-authority:8000",
|
||||||
|
"provider": "http://provider:8002",
|
||||||
|
"rp": "http://relying-party:8001",
|
||||||
|
}
|
||||||
|
return defaults[kind]
|
||||||
|
|
||||||
|
|
||||||
|
TA = env("DJANGO_TRUST_ANCHOR") or infer_base_url("fa")
|
||||||
|
PROVIDER_ISSUER = env("DJANGO_PROVIDER_ISSUER") or f"{infer_base_url('provider')}/oidc/op"
|
||||||
|
RP_BASE = env("DJANGO_RP_BASE_URL") or infer_base_url("rp")
|
||||||
|
RP_ISSUER = f"{RP_BASE}/oidc/rp"
|
||||||
|
PROVIDER_BASE = PROVIDER_ISSUER.removesuffix("/oidc/op")
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_string(value: str) -> str:
|
||||||
|
return (
|
||||||
|
value.replace("http://127.0.0.1:8000/oidc/op", TA + "/oidc/op")
|
||||||
|
.replace("http://127.0.0.1:8001/oidc/rp", RP_ISSUER)
|
||||||
|
.replace("http://127.0.0.1:8002/oidc/op", PROVIDER_ISSUER)
|
||||||
|
.replace("http://127.0.0.1:8000", TA)
|
||||||
|
.replace("http://127.0.0.1:8001", RP_BASE)
|
||||||
|
.replace("http://127.0.0.1:8002", PROVIDER_BASE)
|
||||||
|
.replace("http://provider:8002/oidc/op", PROVIDER_ISSUER)
|
||||||
|
.replace("http://provider:8002", PROVIDER_BASE)
|
||||||
|
.replace("http://relying-party:8001/oidc/rp", RP_ISSUER)
|
||||||
|
.replace("http://relying-party:8001", RP_BASE)
|
||||||
|
.replace("http://federation-authority:8000/oidc/op", TA + "/oidc/op")
|
||||||
|
.replace("http://federation-authority:8000", TA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
REPLACEMENTS = [
|
||||||
|
("http://127.0.0.1:8000/oidc/op", TA + "/oidc/op"),
|
||||||
|
("http://127.0.0.1:8001/oidc/rp", RP_ISSUER),
|
||||||
|
("http://127.0.0.1:8002/oidc/op", PROVIDER_ISSUER),
|
||||||
|
("http://127.0.0.1:8000", TA),
|
||||||
|
("http://127.0.0.1:8001", RP_BASE),
|
||||||
|
("http://127.0.0.1:8002", PROVIDER_BASE),
|
||||||
|
("http://provider:8002/oidc/op", PROVIDER_ISSUER),
|
||||||
|
("http://provider:8002", PROVIDER_BASE),
|
||||||
|
("http://relying-party:8001/oidc/rp", RP_ISSUER),
|
||||||
|
("http://relying-party:8001", RP_BASE),
|
||||||
|
("http://federation-authority:8000/oidc/op", TA + "/oidc/op"),
|
||||||
|
("http://federation-authority:8000", TA),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def deep_replace(value):
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {k: deep_replace(v) for k, v in value.items()}
|
||||||
|
if isinstance(value, list):
|
||||||
|
return [deep_replace(v) for v in value]
|
||||||
|
if isinstance(value, str):
|
||||||
|
return rewrite_string(value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def replace_in_all_text_columns(cur, table: str):
|
||||||
|
cols = cur.execute(f"PRAGMA table_info({table})").fetchall()
|
||||||
|
text_cols = [c[1] for c in cols if c[2] and c[2].upper() in {"TEXT", "VARCHAR(255)", "VARCHAR(2048)", "VARCHAR(64)", "VARCHAR(33)", "JSON"}]
|
||||||
|
for column in text_cols:
|
||||||
|
for old, new in REPLACEMENTS:
|
||||||
|
cur.execute(
|
||||||
|
f"UPDATE {table} SET {column} = REPLACE({column}, ?, ?) WHERE {column} LIKE ?",
|
||||||
|
(old, new, f"%{old}%"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_json_column(cur, table, pk, column):
|
||||||
|
rows = cur.execute(f"SELECT id, {column} FROM {table}").fetchall()
|
||||||
|
for row_id, raw in rows:
|
||||||
|
if not raw:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
data = json.loads(raw)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
updated = deep_replace(data)
|
||||||
|
if updated != data:
|
||||||
|
cur.execute(
|
||||||
|
f"UPDATE {table} SET {column} = ? WHERE {pk} = ?",
|
||||||
|
(json.dumps(updated, ensure_ascii=False), row_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_text_url_column(cur, table, pk, column):
|
||||||
|
rows = cur.execute(f"SELECT {pk}, {column} FROM {table}").fetchall()
|
||||||
|
for row_id, raw in rows:
|
||||||
|
if not raw:
|
||||||
|
continue
|
||||||
|
updated = rewrite_string(raw)
|
||||||
|
if updated != raw:
|
||||||
|
cur.execute(
|
||||||
|
f"UPDATE {table} SET {column} = ? WHERE {pk} = ?",
|
||||||
|
(updated, row_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
raise SystemExit("usage: normalize_db_urls.py <sqlite_db_path>")
|
||||||
|
|
||||||
|
db_path = sys.argv[1]
|
||||||
|
if not os.path.exists(db_path):
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
try:
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
tables = [
|
||||||
|
row[0]
|
||||||
|
for row in cur.execute(
|
||||||
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
||||||
|
).fetchall()
|
||||||
|
]
|
||||||
|
|
||||||
|
for table in tables:
|
||||||
|
replace_in_all_text_columns(cur, table)
|
||||||
|
|
||||||
|
for table in (
|
||||||
|
"spid_cie_oidc_entity_federationentityconfiguration",
|
||||||
|
"spid_cie_oidc_entity_fetchedentitystatement",
|
||||||
|
"spid_cie_oidc_entity_trustchain",
|
||||||
|
):
|
||||||
|
if table not in tables:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if table == "spid_cie_oidc_entity_federationentityconfiguration":
|
||||||
|
update_text_url_column(cur, table, "id", "sub")
|
||||||
|
update_json_column(cur, table, "id", "metadata")
|
||||||
|
update_json_column(cur, table, "id", "authority_hints")
|
||||||
|
update_json_column(cur, table, "id", "trust_marks")
|
||||||
|
update_json_column(cur, table, "id", "trust_mark_issuers")
|
||||||
|
update_json_column(cur, table, "id", "jwks_fed")
|
||||||
|
update_json_column(cur, table, "id", "jwks_core")
|
||||||
|
elif table == "spid_cie_oidc_entity_fetchedentitystatement":
|
||||||
|
update_text_url_column(cur, table, "id", "sub")
|
||||||
|
update_text_url_column(cur, table, "id", "iss")
|
||||||
|
update_json_column(cur, table, "id", "statement")
|
||||||
|
update_text_url_column(cur, table, "id", "jwt")
|
||||||
|
elif table == "spid_cie_oidc_entity_trustchain":
|
||||||
|
update_text_url_column(cur, table, "id", "sub")
|
||||||
|
update_json_column(cur, table, "id", "chain")
|
||||||
|
update_json_column(cur, table, "id", "metadata")
|
||||||
|
update_json_column(cur, table, "id", "jwks")
|
||||||
|
update_json_column(cur, table, "id", "parties_involved")
|
||||||
|
update_json_column(cur, table, "id", "trust_marks")
|
||||||
|
|
||||||
|
conn.commit()
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -17,6 +17,13 @@ def infer_base_url(kind: str) -> str:
|
|||||||
for host in items:
|
for host in items:
|
||||||
if kind in host:
|
if kind in host:
|
||||||
return f"https://{host}"
|
return f"https://{host}"
|
||||||
|
defaults = {
|
||||||
|
"fa": "http://federation-authority:8000",
|
||||||
|
"provider": "http://provider:8002",
|
||||||
|
"rp": "http://relying-party:8001",
|
||||||
|
}
|
||||||
|
if kind in defaults:
|
||||||
|
return defaults[kind]
|
||||||
raise SystemExit(f"missing env for {kind}: set explicit base URL or include hostname in allowed hosts")
|
raise SystemExit(f"missing env for {kind}: set explicit base URL or include hostname in allowed hosts")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user