Release govoplan-access v0.1.25: harden authentication and repair identity mappings
Module Package Release / publish-packages (push) Successful in 15s

This commit is contained in:
2026-09-08 01:32:20 +02:00
parent 0f8a05f8b9
commit 8f8072b4ae
25 changed files with 742 additions and 38 deletions
+64
View File
@@ -0,0 +1,64 @@
# Authentication cache boundary hardening
The authentication cache is an optimization, never an additional authentication
method or permission source. The September 2026 review found and reproduced
three violations of that boundary in isolated SQLite tests.
| Finding | Consequence | Resolution |
| --- | --- | --- |
| Service-account credentials passed through ordinary principal-summary refresh | A narrowed service-account ceiling could be replaced by backing membership roles; the service-account identifier and authentication method were lost. | Dedicated service-account resolution retains its provenance and checks the current ceiling/lifecycle on every request. It does not enter the interactive principal-summary cache. |
| A warmed API-key summary accepted the same secret through a session cookie | The warm path accepted a cookie-authenticated mutation without the CSRF rule applied to real browser sessions; the cold path rejected the credential. | API keys require an explicit Bearer or X-API-Key header on both paths. Session cookies still require matching CSRF cookie/header/hash for mutations. |
| Tenant-key intersection excluded only the historical `system:` spelling | Module-native system permissions and retained module wildcards could survive the tenant-only intersection. | Resolve wildcard grants to concrete registered tenant permissions and exclude system permissions by catalogue and compatibility aliases. |
No existing secrets, sessions, assignments, or database schema are changed.
Normal header-authenticated API keys and concrete tenant aliases remain
compatible. Clients relying on API keys in browser cookies, implicit unknown
wildcards, or accidental instance-level rights must correct their authentication
method or permission configuration; these are not preserved as compatibility
exceptions. Stored grants are not rewritten. Service-account access continues
to narrow immediately when its ceiling is reduced or its lifecycle blocks use.
Regression coverage is in `tests/test_auth_cache_security.py` and
`tests/test_permission_catalog_contract.py`; the tests exercise the full
credential resolver with principal caching enabled, not only the lower-level
API-key lookup. They also verify that valid session CSRF and concrete legacy
tenant aliases still work.
## Remaining coordinated password-change workflow
`Account.password_reset_required` is currently advisory metadata, not an
enforced sign-in restriction. The administrator UI states this limitation,
but the authentication-fields documentation previously claimed mandatory
replacement; its English and German text now reflects the implementation.
A generated password is disclosed once but is not a single-use login secret.
A follow-up must deliver the password-change endpoint, current-password
verification and replacement policy, CSRF and attempt limits, session
revocation/rotation and cache invalidation, a restricted reset-required
principal, and the corresponding accessible UI/recovery path together.
Enabling only a rejection gate would lock affected accounts out without any
supported way to finish the change. This review does not enable such a gate or
change existing passwords.
## Betriebshinweise
API-Schlüssel werden ausschließlich über `Authorization: Bearer` oder
`X-API-Key` gesendet, nicht über das Sitzungscookie. Ändernde Cookie-Anfragen
benötigen weiterhin einen passenden CSRF-Header samt Cookie und serverseitigem
Prüfwert. Dienstkonten behalten ihre eigene Herkunft und werden bei jeder
Anfrage gegen den aktuellen Berechtigungsrahmen und Lebenszyklus geprüft.
Mitgliedschaftsrollen oder zwischengespeicherte interaktive Rechte dürfen diesen
Rahmen nicht ersetzen.
Mandantenschlüssel erhalten keine instanzweiten Rechte, auch nicht unter
modulbezogenen Berechtigungsnamen. Platzhalter werden in konkrete registrierte
Mandantenrechte aufgelöst. Bestehende konkrete Mandantenrechte und ihre
Kompatibilitätsnamen bleiben erhalten; gespeicherte Geheimnisse und
Rollenzuweisungen werden nicht geändert.
Das Kennzeichen `password_reset_required` erzwingt derzeit keinen
Passwortwechsel. Ein einmal angezeigtes Anfangspasswort bleibt zur Anmeldung
verwendbar. Die Nachfolgeumsetzung muss Passwortänderung, eng begrenzten
Zwischenzugriff, Sitzungswechsel beziehungsweise Widerruf und eine bedienbare
Wiederherstellung gemeinsam liefern; eine alleinige Zugriffssperre würde
betroffene Konten ohne durchführbaren Passwortwechsel aussperren.
@@ -0,0 +1,79 @@
# External function mapping schema repair
Access owns `access_external_function_role_assignments`. Some older databases
record the Access baseline (`4a5b6c7d8e9f`) without this table. The mapping list
and `/api/v1/admin/external-function-role-mappings/delta` then fail with an
undefined-table error. This is a schema/history mismatch, not a reason to change
user permissions or recreate tenant data.
Forward repair revision `d8f1b4e7a0c3` follows Access `c7e0a3d6f9b2` on the
release track and `b6d9f2a5c8e1` on the disposable-development track. The latter
also requires Core's existing scope-table rename `4f2a9c8e7b6d`; this is a Core
contract and does not require the optional Tenancy or Organizations modules.
Before applying deployment migrations, back up and verify the database backup.
Use the configured migration track and ordinary deployment migration workflow,
including its deployment-wide advisory lock. Inspect the pending revision plan
before any targeted repair. Never replay or stamp the baseline, initialize dev
data, reset the database, or switch migration tracks to bypass the error.
The development launcher can run pending migrations when its file watcher
reloads the backend. Prepare and test a migration outside the watched source
tree, and complete the backup/preflight before placing a new migration file in
that tree. Do not assume that waiting to invoke a migration command prevents a
running development instance from applying it automatically.
The repair:
- Creates the absent mapping table only, with its baseline columns, role/scope
cascade foreign keys, primary key, tenant/source/function/role uniqueness,
and four lookup indexes.
- Does nothing if the table already exists. It does not alter partial tables;
any other schema mismatch needs separate inspection.
- Never invents mappings or changes roles, memberships, permissions, or other
application records. An empty list means no mappings have been configured.
- Keeps the table and any stored mappings on downgrade, because the table
belongs to the baseline and removing it would delete authorization policy.
After migration, verify both mapping list endpoints return success for an
authorized user in the active tenant, and check the table's constraints and
indexes. Existing read scopes and tenant isolation remain enforced. A missing
table cannot reveal whether historical mappings were once removed: this repair
does not reconstruct lost policy; investigate backups if mappings were expected.
Regression coverage in `tests/test_external_function_mapping_migration.py`
recreates the observed missing-table failure in isolated databases on both
tracks. It checks the HTTP list/delta responses, repeated upgrades, no-op
upgrades with existing mappings, downgrade/re-upgrade preservation, unchanged
parent rows/permissions, constraints, denied unprivileged reads, and tenant
isolation.
## Deutsch
Bei älteren Datenbanken kann die Access-Basismigration als angewendet vermerkt
sein, obwohl `access_external_function_role_assignments` fehlt. Die Liste der
Funktions-Rollenzuordnungen und ihre Delta-API melden dann einen internen Fehler.
Dies ist ein Widerspruch zwischen Schema und Migrationsstand, kein Anlass zur
Erweiterung von Berechtigungen oder zum Neuerstellen von Mandantendaten.
Vor der regulären, vorwärtsgerichteten Migration `d8f1b4e7a0c3` eine überprüfte
Datenbanksicherung erstellen. Den konfigurierten Migrationstrack und den
regulären Bereitstellungsablauf mit installationsweiter Migrationssperre nutzen;
bei einer gezielten Reparatur zuvor die ausstehenden Revisionen prüfen.
Basismigrationen nicht erneut ausführen oder lediglich als angewendet markieren,
keine Entwicklungsdaten initialisieren und die Datenbank nicht zurücksetzen.
Der Entwicklungsstarter kann ausstehende Migrationen bereits beim automatischen
Neuladen des Backends anwenden. Neue Migrationsdateien deshalb außerhalb des
überwachten Quellbaums vorbereiten und testen; Sicherung und Vorprüfung vor dem
Kopieren in den überwachten Quellbaum abschließen. Das Warten mit einem manuellen
Migrationsaufruf verhindert die automatische Anwendung nicht.
Die Reparatur erstellt nur die fehlende Tabelle einschließlich Fremdschlüsseln,
Eindeutigkeitsbedingung und Indizes. Vorhandene Tabellen und Datensätze bleiben
unverändert; auch ein Downgrade entfernt keine Zuordnungsdaten. Teilweise
vorhandene Tabellen werden nicht umgebaut und erfordern eine gesonderte Prüfung.
Es entstehen keine automatischen Zuordnungen oder neuen Rechte. Anschließend
beide Listenendpunkte im aktiven Mandanten mit einer berechtigten Person prüfen.
Eine leere Liste bedeutet, dass keine Zuordnungen konfiguriert sind. Falls früher
Zuordnungen erwartet wurden, Sicherungen prüfen: Verlorene Berechtigungsregeln
lassen sich aus einer fehlenden Tabelle nicht rekonstruieren.
+15
View File
@@ -11,6 +11,21 @@ session is deliberately protected by these operations; use normal logout to end
it. Repeating a revocation is safe. Revoked sessions fail authentication on the
next request, including when a principal summary was previously cached.
The shared WebUI clears reusable API response data on explicit authentication,
account, tenant, and permission transitions, changed session/CSRF cookies, and
authentication-expiry responses. Late reads cannot repopulate caches after those
transitions or after a write finishes. `no-store` responses are not retained;
`no-cache` responses require server revalidation, with ETags retained only where
storage is allowed. Reload bypasses older cached responses. These safeguards do
not erase content already displayed by a page: reload that page to reflect
remote changes. The server remains authoritative for every permission check.
Successful interactive sign-in, including re-login, and local sign-out clear
the saved automation API key. It must not shadow the newly established cookie
session with a different principal. Explicitly applying an API key in connection
settings still selects that credential's identity and triggers a new shell
authentication check. Ordinary profile updates in API-key mode retain the key.
Tenant administrators may list sessions only for a membership in their governed
tenant and may revoke only a session belonging to that membership and tenant.
The mutation requires both the central membership-update permission and an