Kleines FastAPI-Backend zur Generierung, Validierung und Auflösung föderaler URNs.
Der Code ist modular in Pakete gegliedert. Die API-Endpunkte sind aus dem Entrypoint herausgelöst und als Router organisiert:
app/main.py– FastAPI-App, CORS, Startup (DB init), Router-Registrierung, Health-/Ready-Endpunktevcc_urn/api/v1/endpoints.py– versionierte URN-API (generate/validate/store/resolve/search)vcc_urn/api/admin/endpoints.py– Admin-API für Katalog-Management (reload/get/set)vcc_urn/service.py– Service-Layer (Fachlogik URN, DB-Zugriff via Repository, Föderations-Fallback)vcc_urn/repository.py– Repository für Manifeste (SQLAlchemy-Queries, Upsert)vcc_urn/models.py– ORM-Modelle (SQLAlchemy)vcc_urn/schemas.py– Pydantic-Schemas (Request/Response)vcc_urn/db.py– Engine/Session/Base undinit_db()vcc_urn/config.py– Settings (ENV)vcc_urn/security.py– Auth (none/apikey/oidc), Rollen,require_role()vcc_urn/runtime.py– Katalog-Verwaltung zur Laufzeit (reload/set, effective catalogs)
Weitere Aufteilung (z. B. core/, db/-Subpackage) ist vorbereitet und kann schrittweise erfolgen, ohne öffentliche Importe zu brechen.
- Basis:
GET /Info - Health:
GET /healthz,GET /readyz - Versionierte API: Präfix
/api/v1POST /api/v1/generate→ GenerateResponsePOST /api/v1/validate→ ValidateResponsePOST /api/v1/store→ Manifest speichern (Upsert)GET /api/v1/resolve?urn=...→ Manifest oder minimaler StubGET /api/v1/search?uuid=...&limit=&offset=→ SearchResponse
OpenAPI ist unter /docs verfügbar (Swagger UI).
Umgebungsvariablen (optional):
URN_DB_URL(Default: SQLite-Datei im aktuellen Arbeitsverzeichnis) Beispiel für Postgres:
URN_DB_URL=postgresql+psycopg://urn:urn@localhost:5432/urn
URN_NID(Default:de)URN_STATE_RE(Default:^[a-z]{2,3}$)URN_CORS_ORIGINS(Default:*für Entwicklung; Komma-separiert für mehrere Origins)- Föderation:
URN_PEERS(z. B.nrw=https://nrw.example,by=https://by.example) – Basis-URL je Land; der Resolver wird unter/api/v1/resolveerwartet.URN_FED_TIMEOUT(Default:3.0Sekunden) – Timeout für Peer-Anfragen.URN_FED_CACHE_TTL(Default:300Sekunden) – In-Memory TTL Cache für Peer-Results.
- Authentifizierung:
URN_AUTH_MODE(Default:none) –none|apikey|oidcURN_API_KEYS– Komma-separierte API Keys; werden gegen HeaderX-API-Keygeprüft, wennauth_mode=apikey.URN_API_KEYS– Komma-separierte API Keys; werden gegen HeaderX-API-Keygeprüft, wennauth_mode=apikey. Zusätzliche Syntax: Du kannst Rollen pro Key angeben im Formatkey1:admin|user,key2:reader. Beispiel:URN_API_KEYS=admin-key:admin|editor,reader-key:reader.
URN_OIDC_ISSUER,URN_OIDC_AUDIENCE,URN_OIDC_JWKS_URL– OIDC Einstellungen für JWT-Validierung (RS256 via JWKS).URN_OIDC_JWKS_TTL(Default:3600) – Cache-TTL für JWKS (Sekunden).
- Kataloge (optional):
URN_ALLOWED_DOMAINS– Komma-separierte Liste zulässiger Domänen (lowercase). Wenn gesetzt, werden andere Domänen abgewiesen.URN_ALLOWED_OBJ_TYPES– Komma-separierte Liste zulässiger Objekttypen (lowercase). Wenn gesetzt, werden andere Typen abgewiesen.URN_CATALOGS_JSON– JSON-Objekt für landesspezifische Kataloge mit Vorrang vor den globalen Listen. Format:{ "nrw": { "domains": ["bimschg"], "obj_types": ["anlage"] }, "by": { "domains": ["bau"], "obj_types": ["bescheid"] } }Wenn für einstateEinträge vorhanden sind, gelten ausschließlich diese; für andere Länder greift der globale Fallback (URN_ALLOWED_*).
Beispiel .env:
URN_DB_URL=sqlite:///./urn_manifest.db
URN_NID=de
URN_STATE_RE=^[a-z]{2,3}$
URN_CORS_ORIGINS=http://localhost:3000
# Federation
URN_PEERS=nrw=https://nrw.resolver.example,by=https://by.resolver.example
URN_FED_TIMEOUT=3.0
URN_FED_CACHE_TTL=300
# Auth
URN_AUTH_MODE=apikey
URN_API_KEYS=dev-secret-1,dev-secret-2
# OIDC (optional)
# URN_AUTH_MODE=oidc
# URN_OIDC_ISSUER=https://issuer.example/realms/realm
# URN_OIDC_AUDIENCE=api://urn-resolver
# URN_OIDC_JWKS_URL=https://issuer.example/realms/realm/protocol/openid-connect/certs
# URN_OIDC_JWKS_TTL=3600
# Kataloge (optional)
# URN_ALLOWED_DOMAINS=bimschg,bau
# URN_ALLOWED_OBJ_TYPES=anlage,bescheid,gutachten
# URN_CATALOGS_JSON={"nrw":{"domains":["bimschg"],"obj_types":["anlage"]},"by":{"domains":["bau"],"obj_types":["bescheid"]}}
Python 3.10+ und Abhängigkeiten gemäß requirements.txt installieren. Start via Poetry-Skript:
poetry run start
oder direkt:
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
Dieses Backend implementiert die URN-Logik gemäß der begleitenden Strategie und legt Grundlagen für Föderations-Readiness (versionierte API, Health/Ready, CORS, paginierte Suche). Peer-Resolver können konfiguriert werden; bei fehlendem lokalen Manifest wird – falls vorhanden – die zuständige Landesinstanz per /api/v1/resolve angefragt und der Befund lokal gecached. Weitere Schritte (IAM, Gateway/GraphQL Federation, Metriken) folgen.
- Environment konfigurieren (
.env), insbesondereURN_DB_URL(SQLite oder Postgres). - Migrationen anwenden:
Mit Poetry:
poetry run alembic upgrade head
Mit pip/venv:
alembic upgrade head
Neue Migration erzeugen (nach Model-Änderungen):
alembic revision --autogenerate -m "<beschreibung>"
alembic upgrade head
Eine lokale Postgres-Instanz kann per Docker Compose gestartet werden. Danach URN_DB_URL entsprechend setzen.
docker compose up -d postgres
Die folgenden Admin-Routen unterstützen das Katalog-Management zur Laufzeit und sind durch die bestehende Authentifizierung geschützt (API-Key oder OIDC, je nach URN_AUTH_MODE).
GET /admin/catalogs– Liefert die effektiven Kataloge:global_domains,global_obj_types(ausURN_ALLOWED_*)state_catalogs(ausURN_CATALOGS_JSON)
POST /admin/catalogs/reload– Lädt die Konfiguration neu und aktualisiert abhängige Module (z. B. URN-Parser), sodass neue Werte sofort wirken.POST /admin/catalogs/set– SetztURN_CATALOGS_JSONzur Laufzeit (nur im Prozess) und lädt die Konfiguration neu. Payload:
{
"catalogs": {
"nrw": {"domains": ["bimschg"], "obj_types": ["anlage"]},
"by": {"domains": ["bau"], "obj_types": ["bescheid"]}
}
}
Hinweis: Die Admin-Operationen wirken pro Prozess/Instanz. In einem verteilten Setup muss die Konfiguration über ein gemeinsames Backend (z. B. ENV/Secrets-Manager) synchronisiert werden.
Das Projekt unterstützt zwei Haupt-Modi für Zugangssteuerung, konfigurierbar über URN_AUTH_MODE:
none(Default) – keine Authentifizierung, geeignet für Entwicklung.apikey– einfache API-Key-Authentifizierung über HeaderX-API-Key.oidc– OIDC/JWT Validierung gegen JWKS (konfiguriert überURN_OIDC_ISSUER,URN_OIDC_AUDIENCE,URN_OIDC_JWKS_URL).
API-Key Rollen
URN_API_KEYSunterstützt abwärtskompatibel einfache komma-separierte Keys, z. B.key1,key2.- Zusätzliche Syntax erlaubt Rollen pro Key:
key1:admin|editor,key2:reader.- Beispiel:
URN_API_KEYS=admin-key:admin|editor,reader-key:reader - Bei erfolgreicher Auth liefert die Dependency
require_auth()ein Principal mit"roles": [...].
- Beispiel:
OIDC / JWT
- Bei
auth_mode=oidcvalidiert das Backend das Bearer-JWT gegen die konfigurierten JWKS. - Rollen/Claims werden heuristisch extrahiert aus
roles,groups,scopeoderrealm_access.rolesund ebenfalls inprincipal['roles']abgelegt.
Role-based Endpoints
- Für Endpoints, die eine bestimmte Rolle benötigen, steht die Dependency-Factory
require_role("<role>")zur Verfügung.- Beispiel:
dependencies=[Depends(require_role("admin"))]sichert Admin-Router.
- Beispiel:
Entwickler-Hinweis: In Entwicklung ist URN_AUTH_MODE=none praktisch; require_role erlaubt in diesem Fall weiterhin Zugriff (Komfort für Tests). In Produktion sollte none niemals verwendet werden.
- Starlette meldet eine PendingDeprecationWarning zur Multipart-Implementierung; zur Beseitigung solltest du
python-multipartin deiner Umgebung installieren (ich habe es inrequirements.txtergänzt):
& "C:/Program Files/Python313/python.exe" -m pip install python-multipart- httpx gibt gelegentlich eine DeprecationWarning über den
app-Shortcut aus; das betrifft Fällen in denenhttpx.Client(app=fastapi_app)verwendet wird. Die empfohlene Alternative ist:
from httpx import WSGITransport
client = httpx.Client(transport=WSGITransport(app=fastapi_app))Diese Warnungen sind nicht kritisch, aber das Aufräumen reduziert Rauschen in CI.
Eine Beispiel-Datei .env.example wurde beigefügt mit sinnvollen Defaults und Beispielen (inkl. API-Key-Rollen-Syntax).