diff --git a/package.json b/package.json index cafcadc..289ca1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/access-webui", - "version": "0.1.20", + "version": "0.1.21", "private": true, "type": "module", "main": "webui/src/index.ts", diff --git a/pyproject.toml b/pyproject.toml index 213b3c1..46b4e4e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-access" -version = "0.1.20" +version = "0.1.21" description = "GovOPlaN access platform module with identity, auth, RBAC, and scope primitives." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_access/__init__.py b/src/govoplan_access/__init__.py index 61c20a8..b7eabf5 100644 --- a/src/govoplan_access/__init__.py +++ b/src/govoplan_access/__init__.py @@ -1,3 +1,3 @@ """GovOPlaN access platform module.""" -__version__ = "0.1.20" +__version__ = "0.1.21" diff --git a/src/govoplan_access/backend/manifest.py b/src/govoplan_access/backend/manifest.py index b4ddc42..3a9a198 100644 --- a/src/govoplan_access/backend/manifest.py +++ b/src/govoplan_access/backend/manifest.py @@ -2,7 +2,9 @@ from __future__ import annotations from pathlib import Path -from govoplan_access.backend.configuration_provider import ACCESS_CONFIGURATION_CAPABILITY +from govoplan_access.backend.configuration_provider import ( + ACCESS_CONFIGURATION_CAPABILITY, +) from govoplan_access.backend.dsar_provider import ACCESS_DSAR_CAPABILITY from govoplan_access.backend.db.base import AccessBase from govoplan_access.backend.db import models as access_models # noqa: F401 - populate access metadata @@ -28,7 +30,10 @@ from govoplan_core.core.campaigns import CAPABILITY_CAMPAIGNS_ACCESS from govoplan_core.core.files import CAPABILITY_FILES_ACCESS from govoplan_core.core.identity import CAPABILITY_IDENTITY_DIRECTORY, IdentityDirectory from govoplan_core.core.idm import CAPABILITY_IDM_DIRECTORY, IdmDirectory -from govoplan_core.core.organizations import CAPABILITY_ORGANIZATION_DIRECTORY, OrganizationDirectory +from govoplan_core.core.organizations import ( + CAPABILITY_ORGANIZATION_DIRECTORY, + OrganizationDirectory, +) from govoplan_core.core.module_guards import persistent_table_uninstall_guard from govoplan_core.core.provider_governance import declared_module_architecture from govoplan_core.core.modules import ( @@ -51,7 +56,9 @@ from govoplan_core.core.references import CAPABILITY_ACCESS_REFERENCE_OPTIONS from govoplan_core.core.views import ViewSurface -def _permission(scope: str, label: str, description: str, category: str, level: str) -> PermissionDefinition: +def _permission( + scope: str, label: str, description: str, category: str, level: str +) -> PermissionDefinition: module_id, resource, action = scope.split(":", 2) return PermissionDefinition( scope=scope, @@ -66,55 +73,349 @@ def _permission(scope: str, label: str, description: str, category: str, level: ACCESS_PERMISSIONS: tuple[PermissionDefinition, ...] = ( - _permission("access:tenant:read", "View tenants", "List and inspect tenant registry entries.", "Access", "system"), - _permission("access:tenant:create", "Create tenants", "Create tenant registry entries.", "Access", "system"), - _permission("access:tenant:update", "Update tenants", "Update tenant metadata and activation state.", "Access", "system"), - _permission("access:tenant:suspend", "Suspend tenants", "Activate or suspend tenant spaces while preserving evidence.", "Access", "system"), - _permission("access:account:read", "View accounts", "List and inspect global login accounts.", "Access", "system"), - _permission("access:account:create", "Create accounts", "Create global login accounts.", "Access", "system"), - _permission("access:account:update", "Update accounts", "Update or suspend global login accounts.", "Access", "system"), - _permission("access:account:suspend", "Suspend accounts", "Activate or suspend global login accounts while preserving a system owner.", "Access", "system"), - _permission("access:system_role:read", "View system roles", "Inspect instance-wide role definitions and their permissions.", "Access", "system"), - _permission("access:system_role:write", "Define system roles", "Create and edit instance-wide role definitions within delegation limits.", "Access", "system"), - _permission("access:system_role:assign", "Assign system roles", "Assign instance-wide roles to accounts while preserving a system owner.", "Access", "system"), - _permission("access:system_setting:read", "View system settings", "Read instance defaults and tenant-governance defaults.", "Access", "system"), - _permission("access:system_setting:write", "Manage system settings", "Change instance defaults and tenant-governance defaults.", "Access", "system"), - _permission("access:system_credential:read", "View system credentials", "List instance-wide reusable credential envelopes without revealing secret values.", "Access", "system"), - _permission("access:system_credential:write", "Manage system credentials", "Create, update, and retire instance-wide reusable credential envelopes.", "Access", "system"), - _permission("access:maintenance:access", "Access during maintenance", "Use the system while maintenance mode is active.", "Access", "system"), - _permission("access:audit:read", "View system audit", "Read audit records across tenants.", "Access", "system"), - _permission("access:membership:read", "View memberships", "List tenant memberships and effective access.", "Tenant access", "tenant"), - _permission("access:membership:create", "Create memberships", "Create tenant-local account memberships.", "Tenant access", "tenant"), - _permission("access:membership:update", "Update memberships", "Update or suspend tenant memberships.", "Tenant access", "tenant"), - _permission("access:session:manage_own", "Manage own sessions", "Inspect and revoke the current account's browser sessions without exposing credentials.", "Tenant access", "tenant"), - _permission("access:group:read", "View groups", "List tenant groups and members.", "Tenant access", "tenant"), - _permission("access:group:write", "Manage groups", "Create and update tenant groups.", "Tenant access", "tenant"), - _permission("access:group:manage_members", "Manage group members", "Add and remove memberships from groups.", "Tenant access", "tenant"), - _permission("access:role:read", "View roles", "Inspect tenant and system role definitions.", "Tenant access", "tenant"), - _permission("access:role:write", "Manage roles", "Create and update assignable roles.", "Tenant access", "tenant"), - _permission("access:role:assign", "Assign roles", "Bind roles to memberships, groups, accounts or services.", "Tenant access", "tenant"), - _permission("access:function:read", "View functions", "Inspect organization-bound functions and assignments.", "Tenant access", "tenant"), - _permission("access:function:write", "Manage functions", "Create and update organization units, functions, and role mappings.", "Tenant access", "tenant"), - _permission("access:function:assign", "Assign functions", "Assign organization-bound functions to accounts.", "Tenant access", "tenant"), - _permission("access:function:delegate", "Delegate functions", "Create and revoke permitted function delegations.", "Tenant access", "tenant"), - _permission("access:api_key:read", "View API keys", "List API keys without revealing secrets.", "Tenant access", "tenant"), - _permission("access:api_key:create", "Create API keys", "Create tenant API keys within delegation limits.", "Tenant access", "tenant"), - _permission("access:api_key:revoke", "Revoke API keys", "Revoke tenant API keys.", "Tenant access", "tenant"), - _permission("access:service_account:read", "View service accounts", "List non-login automation principals and their current scope ceilings.", "Tenant access", "tenant"), - _permission("access:service_account:write", "Manage service accounts", "Create, update, suspend, and retire scope-bounded automation principals.", "Tenant access", "tenant"), - _permission("access:setting:read", "View settings", "Read access and governance settings.", "Tenant access", "tenant"), - _permission("access:setting:write", "Manage settings", "Update access and governance settings.", "Tenant access", "tenant"), - _permission("access:credential:read", "View credentials", "List reusable credential envelopes without revealing secret values.", "Tenant access", "tenant"), - _permission("access:credential:write", "Manage credentials", "Create, update, and retire reusable credential envelopes.", "Tenant access", "tenant"), - _permission("access:credential:manage_own", "Manage own credentials", "Manage reusable credentials owned by the current membership.", "Tenant access", "tenant"), - _permission("access:policy:read", "View tenant policies", "Read tenant policy and governance settings.", "Tenant access", "tenant"), - _permission("access:policy:write", "Manage tenant policies", "Change tenant policy and governance settings where system policy permits it.", "Tenant access", "tenant"), - _permission("access:privacy:read", "View data-subject requests", "Inspect tenant data-subject requests, provider coverage, and retained evidence decisions.", "Privacy", "tenant"), - _permission("access:privacy:manage", "Manage data-subject requests", "Create requests and run provider searches and erasure planning.", "Privacy", "tenant"), - _permission("access:privacy:export", "Export data-subject requests", "Export the collected personal-data package and its coverage manifest.", "Privacy", "tenant"), - _permission("access:privacy:erase", "Execute data erasure", "Execute explicitly selected, provider-owned erasure and anonymization actions.", "Privacy", "tenant"), - _permission("access:governance:read", "View governance", "Inspect managed role and group templates.", "Access", "system"), - _permission("access:governance:write", "Manage governance", "Create and assign managed role and group templates.", "Access", "system"), + _permission( + "access:tenant:read", + "View tenants", + "List and inspect tenant registry entries.", + "Access", + "system", + ), + _permission( + "access:tenant:create", + "Create tenants", + "Create tenant registry entries.", + "Access", + "system", + ), + _permission( + "access:tenant:update", + "Update tenants", + "Update tenant metadata and activation state.", + "Access", + "system", + ), + _permission( + "access:tenant:suspend", + "Suspend tenants", + "Activate or suspend tenant spaces while preserving evidence.", + "Access", + "system", + ), + _permission( + "access:account:read", + "View accounts", + "List and inspect global login accounts.", + "Access", + "system", + ), + _permission( + "access:account:create", + "Create accounts", + "Create global login accounts.", + "Access", + "system", + ), + _permission( + "access:account:update", + "Update accounts", + "Update or suspend global login accounts.", + "Access", + "system", + ), + _permission( + "access:account:suspend", + "Suspend accounts", + "Activate or suspend global login accounts while preserving a system owner.", + "Access", + "system", + ), + _permission( + "access:system_role:read", + "View system roles", + "Inspect instance-wide role definitions and their permissions.", + "Access", + "system", + ), + _permission( + "access:system_role:write", + "Define system roles", + "Create and edit instance-wide role definitions within delegation limits.", + "Access", + "system", + ), + _permission( + "access:system_role:assign", + "Assign system roles", + "Assign instance-wide roles to accounts while preserving a system owner.", + "Access", + "system", + ), + _permission( + "access:system_setting:read", + "View system settings", + "Read instance defaults and tenant-governance defaults.", + "Access", + "system", + ), + _permission( + "access:system_setting:write", + "Manage system settings", + "Change instance defaults and tenant-governance defaults.", + "Access", + "system", + ), + _permission( + "access:system_credential:read", + "View system credentials", + "List instance-wide reusable credential envelopes without revealing secret values.", + "Access", + "system", + ), + _permission( + "access:system_credential:write", + "Manage system credentials", + "Create, update, and retire instance-wide reusable credential envelopes.", + "Access", + "system", + ), + _permission( + "access:maintenance:access", + "Access during maintenance", + "Use the system while maintenance mode is active.", + "Access", + "system", + ), + _permission( + "access:audit:read", + "View system audit", + "Read audit records across tenants.", + "Access", + "system", + ), + _permission( + "access:membership:read", + "View memberships", + "List tenant memberships and effective access.", + "Tenant access", + "tenant", + ), + _permission( + "access:membership:create", + "Create memberships", + "Create tenant-local account memberships.", + "Tenant access", + "tenant", + ), + _permission( + "access:membership:update", + "Update memberships", + "Update or suspend tenant memberships.", + "Tenant access", + "tenant", + ), + _permission( + "access:session:manage_own", + "Manage own sessions", + "Inspect and revoke the current account's browser sessions without exposing credentials.", + "Tenant access", + "tenant", + ), + _permission( + "access:group:read", + "View groups", + "List tenant groups and members.", + "Tenant access", + "tenant", + ), + _permission( + "access:group:write", + "Manage groups", + "Create and update tenant groups.", + "Tenant access", + "tenant", + ), + _permission( + "access:group:manage_members", + "Manage group members", + "Add and remove memberships from groups.", + "Tenant access", + "tenant", + ), + _permission( + "access:role:read", + "View roles", + "Inspect tenant and system role definitions.", + "Tenant access", + "tenant", + ), + _permission( + "access:role:write", + "Manage roles", + "Create and update assignable roles.", + "Tenant access", + "tenant", + ), + _permission( + "access:role:assign", + "Assign roles", + "Bind roles to memberships, groups, accounts or services.", + "Tenant access", + "tenant", + ), + _permission( + "access:function:read", + "View functions", + "Inspect organization-bound functions and assignments.", + "Tenant access", + "tenant", + ), + _permission( + "access:function:write", + "Manage functions", + "Create and update organization units, functions, and role mappings.", + "Tenant access", + "tenant", + ), + _permission( + "access:function:assign", + "Assign functions", + "Assign organization-bound functions to accounts.", + "Tenant access", + "tenant", + ), + _permission( + "access:function:delegate", + "Delegate functions", + "Create and revoke permitted function delegations.", + "Tenant access", + "tenant", + ), + _permission( + "access:api_key:read", + "View API keys", + "List API keys without revealing secrets.", + "Tenant access", + "tenant", + ), + _permission( + "access:api_key:create", + "Create API keys", + "Create tenant API keys within delegation limits.", + "Tenant access", + "tenant", + ), + _permission( + "access:api_key:revoke", + "Revoke API keys", + "Revoke tenant API keys.", + "Tenant access", + "tenant", + ), + _permission( + "access:service_account:read", + "View service accounts", + "List non-login automation principals and their current scope ceilings.", + "Tenant access", + "tenant", + ), + _permission( + "access:service_account:write", + "Manage service accounts", + "Create, update, suspend, and retire scope-bounded automation principals.", + "Tenant access", + "tenant", + ), + _permission( + "access:setting:read", + "View settings", + "Read access and governance settings.", + "Tenant access", + "tenant", + ), + _permission( + "access:setting:write", + "Manage settings", + "Update access and governance settings.", + "Tenant access", + "tenant", + ), + _permission( + "access:credential:read", + "View credentials", + "List reusable credential envelopes without revealing secret values.", + "Tenant access", + "tenant", + ), + _permission( + "access:credential:write", + "Manage credentials", + "Create, update, and retire reusable credential envelopes.", + "Tenant access", + "tenant", + ), + _permission( + "access:credential:manage_own", + "Manage own credentials", + "Manage reusable credentials owned by the current membership.", + "Tenant access", + "tenant", + ), + _permission( + "access:policy:read", + "View tenant policies", + "Read tenant policy and governance settings.", + "Tenant access", + "tenant", + ), + _permission( + "access:policy:write", + "Manage tenant policies", + "Change tenant policy and governance settings where system policy permits it.", + "Tenant access", + "tenant", + ), + _permission( + "access:privacy:read", + "View data-subject requests", + "Inspect tenant data-subject requests, provider coverage, and retained evidence decisions.", + "Privacy", + "tenant", + ), + _permission( + "access:privacy:manage", + "Manage data-subject requests", + "Create requests and run provider searches and erasure planning.", + "Privacy", + "tenant", + ), + _permission( + "access:privacy:export", + "Export data-subject requests", + "Export the collected personal-data package and its coverage manifest.", + "Privacy", + "tenant", + ), + _permission( + "access:privacy:erase", + "Execute data erasure", + "Execute explicitly selected, provider-owned erasure and anonymization actions.", + "Privacy", + "tenant", + ), + _permission( + "access:governance:read", + "View governance", + "Inspect managed role and group templates.", + "Access", + "system", + ), + _permission( + "access:governance:write", + "Manage governance", + "Create and assign managed role and group templates.", + "Access", + "system", + ), ) ACCESS_ROLE_TEMPLATES: tuple[RoleTemplate, ...] = ( @@ -323,9 +624,41 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( documentation_types=("admin", "user"), audience=("user", "tenant_admin", "system_admin"), order=8, - links=(DocumentationLink(label="Profile API", href="/api/v1/auth/profile", kind="api"),), + links=( + DocumentationLink( + label="Profile API", href="/api/v1/auth/profile", kind="api" + ), + ), related_modules=("admin", "tenancy"), - metadata={"kind": "reference", "help_contexts": ["core.settings", "admin.system-settings", "tenancy.admin.tenant-settings"]}, + translations={ + "de": { + "title": "Quelle des wirksamen Erscheinungsbilds verstehen", + "summary": ( + "Das authentifizierte Profil erläutert die wirksame Farbpalette, " + "Richtliniensperren und gesteuerte persönliche Designanpassungen." + ), + "body": ( + "Eine ausdrücklich gewählte persönliche Farbpalette hat normalerweise Vorrang vor Mandanten- und Systemvorgaben. " + "Beim Zurücksetzen wird die Vererbung gespeichert, nicht eine Kopie des aktuellen Standardwerts. Eine Sperre auf " + "Mandantenebene unterdrückt persönliche Einstellungen; eine Systemsperre unterdrückt sowohl Mandanten- als auch " + "persönliche Einstellungen. Access setzt die Sperre beim Schreiben des Profils durch und liefert in vollständigen " + "Profilantworten die wirksame Palette, ihre Quelle, den geerbten Wert und den Sperrstatus. Erweiterte Anpassungen für " + "Akzent-, Flächen- und Statusfarben werden nur angenommen, wenn das System sie zulässt, der Mandant sie nicht blockiert " + "und keine der beiden Palettenebenen gesperrt ist. Die Dokumente für den hellen und dunklen Modus sind versioniert und " + "werden atomar auf hexadezimale Farbwerte, WCAG-AA-Kontrast der zugehörigen Paare und unterscheidbare Statusfarben geprüft. " + "Ungültige oder unzulässige Dokumente werden niemals teilweise angewendet; eine inaktive gespeicherte Anpassung kann " + "weiterhin entfernt werden, um zur Vererbung zurückzukehren." + ), + } + }, + metadata={ + "kind": "reference", + "help_contexts": [ + "core.settings", + "admin.system-settings", + "tenancy.admin.tenant-settings", + ], + }, ), DocumentationTopic( id="access.reference.resource-explanation-subjects", @@ -373,6 +706,23 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), related_modules=("audit", "campaigns", "files", "policy"), + translations={ + "de": { + "title": "Benutzende für die Ressourcenzugriffsdiagnose auswählen", + "summary": ( + "Zugriffserklärungen beziehen sich standardmäßig auf die angemeldete Person und zeigen andere Benutzende " + "des Mandanten nur, wenn Policy diese Diagnose erlaubt." + ), + "body": ( + "Der gemeinsame Erklärungsdialog von Files und Campaign fragt Access nach den zulässigen Zielpersonen. Ist Policy " + "nicht verfügbar oder fehlt der handelnden Person policy:access_explanation:select_user, liefert Access nur die aktive " + "Mitgliedschaft der angemeldeten Person und keine Metadaten anderer Benutzender. Erlaubt Policy die Auswahl, bleibt sie " + "auf aktive Benutzende des aktuellen Mandanten begrenzt. Jede Erklärung für eine andere Person erzeugt einen Auditnachweis " + "mit Zielmitgliedschaft, Ressource, angeforderter Aktion und Richtlinienquelle. Die Erklärung dient ausschließlich der " + "Diagnose und gewährt niemals Ressourcenzugriff." + ), + } + }, metadata={ "kind": "reference", "help_contexts": ["access.resource-explanation.subject"], @@ -403,9 +753,21 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Configuration package preflight", href="/api/v1/admin/configuration-packages/dry-run", kind="api"), - DocumentationLink(label="Configuration package apply", href="/api/v1/admin/configuration-packages/apply", kind="api"), - DocumentationLink(label="Configuration package export", href="/api/v1/admin/configuration-packages/export", kind="api"), + DocumentationLink( + label="Configuration package preflight", + href="/api/v1/admin/configuration-packages/dry-run", + kind="api", + ), + DocumentationLink( + label="Configuration package apply", + href="/api/v1/admin/configuration-packages/apply", + kind="api", + ), + DocumentationLink( + label="Configuration package export", + href="/api/v1/admin/configuration-packages/export", + kind="api", + ), ), related_modules=("admin", "audit", "ops"), translations={ @@ -449,9 +811,38 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( audience=("operator", "system_admin"), order=10, links=( - DocumentationLink(label="Bootstrap readiness API", href="/api/v1/bootstrap/status", kind="api"), - DocumentationLink(label="First-administrator enrollment API", href="/api/v1/bootstrap/first-admin", kind="api"), + DocumentationLink( + label="Bootstrap readiness API", + href="/api/v1/bootstrap/status", + kind="api", + ), + DocumentationLink( + label="First-administrator enrollment API", + href="/api/v1/bootstrap/first-admin", + kind="api", + ), ), + translations={ + "de": { + "title": "Erste Produktionsadministration einrichten", + "summary": ( + "Eine lokale Betriebsperson kann genau einen ablaufenden Berechtigungsnachweis ausstellen, solange noch keine dauerhafte " + "Systemadministration besteht, und ihn einmalig zum Anlegen des geschützten Systemkontos und einer ersten " + "Mandantenmitgliedschaft verwenden." + ), + "body": ( + "Führen Sie nach den Migrationen und der Installation von Access den Core-Befehl zum Ausstellen des ersten " + "Administrationszugangs aus. Der Befehl speichert das zufällige Geheimnis in einer lokalen Datei mit Modus 0600 und gibt " + "nur Pfad, Fingerabdruck und Ablaufzeitpunkt aus. Der öffentliche Bootstrap-Statusendpunkt zeigt ausschließlich die " + "minimale Bereitschaft. Der Einrichtungsendpunkt akzeptiert nur die Felder des ersten Kontos und des ersten Mandanten, " + "legt atomar ein geschütztes System-Owner-Konto mit einer Tenant-Owner-Mitgliedschaft an und zieht den Nachweis ein. " + "Identische Wiederholungen liefern das bereits angelegte Konto zurück, ohne einen weiteren Owner zu erzeugen. Verlorenes " + "oder abgelaufenes Material kann nur mit dem lokalen Wiederherstellungsbefehl erneuert werden und nur solange die Prüfung " + "keine dauerhafte Administration findet. Entwicklungs-Bootstrap-Einstellungen sind ein eigener, ausschließlich für die " + "Entwicklung bestimmter Pfad und werden durch die Einrichtung niemals aktiviert." + ), + } + }, metadata={ "kind": "operator_workflow", "commands": [ @@ -491,12 +882,34 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Access administration", href="/admin", kind="runtime"), - DocumentationLink(label="Users API", href="/api/v1/admin/users", kind="api"), - DocumentationLink(label="Groups API", href="/api/v1/admin/groups", kind="api"), - DocumentationLink(label="Roles API", href="/api/v1/admin/roles", kind="api"), + DocumentationLink( + label="Access administration", href="/admin", kind="runtime" + ), + DocumentationLink( + label="Users API", href="/api/v1/admin/users", kind="api" + ), + DocumentationLink( + label="Groups API", href="/api/v1/admin/groups", kind="api" + ), + DocumentationLink( + label="Roles API", href="/api/v1/admin/roles", kind="api" + ), ), configuration_keys=("access_governance",), + translations={ + "de": { + "title": "Einer Person Zugriff gewähren", + "summary": ( + "In der Zugriffsverwaltung eine Mandantenmitgliedschaft anlegen oder aktualisieren, die Person Gruppen zuordnen und " + "nur die benötigten Rollen vergeben." + ), + "body": ( + "Suchen Sie die Person oder legen Sie sie an, prüfen Sie ihre vorhandene Mitgliedschaft und gewähren Sie den Zugriff " + "anschließend über Gruppen und Rollen. Ist eine Rolle oder Gruppe nicht verfügbar, können die geltenden " + "Governance-Regeln oder die eigene Delegationsgrenze die Änderung blockieren." + ), + } + }, metadata={ "kind": "workflow", "help_contexts": [ @@ -551,10 +964,39 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Governance templates", href="/admin?section=system-role-templates", kind="runtime"), - DocumentationLink(label="Bulk synchronization API", href="/api/v1/admin/system/governance-templates/synchronize", kind="api"), - DocumentationLink(label="Access module boundary", href="docs/ACCESS_MODULE_BOUNDARY.md", kind="repository"), + DocumentationLink( + label="Governance templates", + href="/admin?section=system-role-templates", + kind="runtime", + ), + DocumentationLink( + label="Bulk synchronization API", + href="/api/v1/admin/system/governance-templates/synchronize", + kind="api", + ), + DocumentationLink( + label="Access module boundary", + href="docs/ACCESS_MODULE_BOUNDARY.md", + kind="repository", + ), ), + translations={ + "de": { + "title": "Gesteuerte Gruppen und Rollen gesammelt abgleichen", + "summary": ( + "Access überführt zentral zugewiesene Governance-Vorlagen über einen begrenzten, versionierten Abgleichvertrag in " + "Mandantengruppen und -rollen." + ), + "body": ( + "Admin übergibt stabile Vorlagen- und Mandantenzuweisungen an die Access-Fähigkeit Governance Projection v1. Access lädt " + "gesteuerte Gruppen, Rollen, Mitgliedschaften und Rollenzuordnungen gesammelt und liefert je Zuweisung genau ein Ergebnis: " + "angelegt, aktualisiert, unverändert, entfernt, nicht vorhanden, blockiert oder fehlgeschlagen. Derselbe Auftrag kann " + "idempotent wiederholt werden. Eine Entfernung bleibt blockiert, solange die gesteuerte Ressource Mitglieder oder " + "Rollenzuweisungen besitzt; geschützte oder mandantenfremde Ressourcen werden niemals übernommen. Trockenläufe berechnen " + "dieselben Ergebnisse ohne Änderungen. Vorschau und Anwendung halten Quelle und Zuweisungsmodus im Admin-Auditereignis fest." + ), + } + }, metadata={ "kind": "operator_workflow", "help_contexts": ["admin.governance-templates"], @@ -596,15 +1038,48 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Access administration", href="/admin", kind="runtime"), - DocumentationLink(label="Users API", href="/api/v1/admin/users", kind="api"), - DocumentationLink(label="Groups API", href="/api/v1/admin/groups", kind="api"), - DocumentationLink(label="Roles API", href="/api/v1/admin/roles", kind="api"), - DocumentationLink(label="API keys API", href="/api/v1/admin/api-keys", kind="api"), - DocumentationLink(label="Service accounts", href="/admin?section=tenant-service-accounts", kind="runtime"), - DocumentationLink(label="Service accounts API", href="/api/v1/admin/service-accounts", kind="api"), + DocumentationLink( + label="Access administration", href="/admin", kind="runtime" + ), + DocumentationLink( + label="Users API", href="/api/v1/admin/users", kind="api" + ), + DocumentationLink( + label="Groups API", href="/api/v1/admin/groups", kind="api" + ), + DocumentationLink( + label="Roles API", href="/api/v1/admin/roles", kind="api" + ), + DocumentationLink( + label="API keys API", href="/api/v1/admin/api-keys", kind="api" + ), + DocumentationLink( + label="Service accounts", + href="/admin?section=tenant-service-accounts", + kind="runtime", + ), + DocumentationLink( + label="Service accounts API", + href="/api/v1/admin/service-accounts", + kind="api", + ), ), configuration_keys=("access_governance",), + translations={ + "de": { + "title": "Felder der Zugriffsverwaltung", + "summary": ( + "Die Zugriffsverwaltung zeigt Mandantenmitgliedschaften, Gruppen, Rollen und API-Schlüssel; die Admin-Dokumentation " + "ordnet sichtbare Felder den API-Nutzdaten und Berechtigungen zu." + ), + "body": ( + "Benutzende benötigen verständliche Bezeichnungen und kurze Erläuterungen. Administrierende benötigen zusätzlich Route, " + "API-Feld, Berechtigung und Governance-Hinweis, um nicht verfügbare Aktionen zu diagnostizieren. Neue Geheimnisse für " + "API-Schlüssel tragen die GovOPlaN-Kennung `gpn_`; bereits ausgestellte Schlüssel behalten ihren ursprünglichen Wert und " + "bleiben bis zum Ablauf oder Widerruf gültig." + ), + } + }, metadata={ "kind": "reference", "help_contexts": [ @@ -707,8 +1182,14 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Tenant API keys", href="/admin?section=tenant-api-keys", kind="runtime"), - DocumentationLink(label="API keys API", href="/api/v1/admin/api-keys", kind="api"), + DocumentationLink( + label="Tenant API keys", + href="/admin?section=tenant-api-keys", + kind="runtime", + ), + DocumentationLink( + label="API keys API", href="/api/v1/admin/api-keys", kind="api" + ), ), translations={ "de": { @@ -784,9 +1265,19 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="System credentials", href="/admin?section=system-credentials", kind="runtime"), - DocumentationLink(label="Tenant credentials", href="/admin?section=tenant-credentials", kind="runtime"), - DocumentationLink(label="Personal credentials", href="/settings", kind="runtime"), + DocumentationLink( + label="System credentials", + href="/admin?section=system-credentials", + kind="runtime", + ), + DocumentationLink( + label="Tenant credentials", + href="/admin?section=tenant-credentials", + kind="runtime", + ), + DocumentationLink( + label="Personal credentials", href="/settings", kind="runtime" + ), ), translations={ "de": { @@ -868,9 +1359,21 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Service accounts", href="/admin?section=tenant-service-accounts", kind="runtime"), - DocumentationLink(label="Service accounts API", href="/api/v1/admin/service-accounts", kind="api"), - DocumentationLink(label="Credential lifecycle API", href="/api/v1/admin/service-accounts/{service_account_id}/credentials", kind="api"), + DocumentationLink( + label="Service accounts", + href="/admin?section=tenant-service-accounts", + kind="runtime", + ), + DocumentationLink( + label="Service accounts API", + href="/api/v1/admin/service-accounts", + kind="api", + ), + DocumentationLink( + label="Credential lifecycle API", + href="/api/v1/admin/service-accounts/{service_account_id}/credentials", + kind="api", + ), ), translations={ "de": { @@ -944,8 +1447,28 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( order=85, related_modules=("admin", "tenancy", "views"), links=( - DocumentationLink(label="Workspace settings", href="/settings?section=workspace", kind="runtime"), + DocumentationLink( + label="Workspace settings", + href="/settings?section=workspace", + kind="runtime", + ), ), + translations={ + "de": { + "title": "Seitenleiste persönlich anpassen", + "summary": ( + "Benutzende können verfügbare Navigationseinträge neu anordnen oder ausblenden, ohne Zugriffsrechte oder die " + "Arbeitsbereiche anderer Personen zu verändern." + ), + "body": ( + "Öffnen Sie die Einstellungen und verschieben oder zeigen Sie Einträge im Editor für die Arbeitsbereichsnavigation. " + "Persönliche Reihenfolge und Sichtbarkeit haben Vorrang vor Mandanten- und Systempräferenzen. Vom System oder Mandanten " + "gesperrte Einträge bleiben sichtbar; Benutzende können selbst keine Sperre erzeugen. Die Auswahl der geerbten Reihenfolge " + "entfernt die persönliche Ebene. Modulberechtigung, View-Richtlinie und Zugriffsrechte bestimmen weiterhin, welche Ziele " + "verfügbar sind. Eine Navigationsänderung gewährt daher niemals zusätzlichen Zugriff." + ), + } + }, metadata={ "kind": "reference", "help_contexts": ["core.settings.workspace"], @@ -972,9 +1495,19 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Sessions and devices", href="/settings?section=sessions", kind="runtime"), - DocumentationLink(label="Own sessions API", href="/api/v1/auth/sessions", kind="api"), - DocumentationLink(label="Session management reference", href="docs/SESSION_MANAGEMENT.md", kind="repository"), + DocumentationLink( + label="Sessions and devices", + href="/settings?section=sessions", + kind="runtime", + ), + DocumentationLink( + label="Own sessions API", href="/api/v1/auth/sessions", kind="api" + ), + DocumentationLink( + label="Session management reference", + href="docs/SESSION_MANAGEMENT.md", + kind="repository", + ), ), translations={ "de": { @@ -1032,17 +1565,66 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( conditions=( DocumentationCondition( required_modules=("access", "organizations"), - any_scopes=("access:function:read", "access:role:read", "admin:roles:read"), + any_scopes=( + "access:function:read", + "access:role:read", + "admin:roles:read", + ), ), ), links=( - DocumentationLink(label="Function role mappings", href="/admin?section=tenant-function-role-mappings", kind="runtime"), - DocumentationLink(label="External function role mappings API", href="/api/v1/admin/external-function-role-mappings", kind="api"), - DocumentationLink(label="Effective user access explanation API", href="/api/v1/admin/users/{user_id}/access-explanation", kind="api"), - DocumentationLink(label="Organizations functions", href="/organizations?section=functions", kind="runtime"), + DocumentationLink( + label="Function role mappings", + href="/admin?section=tenant-function-role-mappings", + kind="runtime", + ), + DocumentationLink( + label="External function role mappings API", + href="/api/v1/admin/external-function-role-mappings", + kind="api", + ), + DocumentationLink( + label="Effective user access explanation API", + href="/api/v1/admin/users/{user_id}/access-explanation", + kind="api", + ), + DocumentationLink( + label="Organizations functions", + href="/organizations?section=functions", + kind="runtime", + ), DocumentationLink(label="IDM assignments", href="/idm", kind="runtime"), - DocumentationLink(label="Acting contexts API", href="/api/v1/auth/acting-contexts", kind="api"), + DocumentationLink( + label="Acting contexts API", + href="/api/v1/auth/acting-contexts", + kind="api", + ), ), + translations={ + "de": { + "title": "Organisationsfunktionen und Zugriffsrollen", + "summary": ( + "Organizations definiert Funktionsmerkmale, IDM weist sie Identitäten zu und Access bildet akzeptierte Merkmale auf " + "Rollen und Berechtigungen ab." + ), + "body": ( + "Organizations besitzt das Organisationsmetamodell sowie konkrete Einheiten, Strukturen und Funktionen. IDM besitzt die " + "Tatsache, dass eine Identität über eines ihrer Konten eine Funktion in einer Organisationseinheit innehat, einschließlich " + "delegierter und stellvertretender Zuweisungen. Access übernimmt diese bestätigten IDM-Merkmale über die Directory-Fähigkeit, " + "prüft Funktionskennungen gegen Organizations und leitet daraus nur dann Rechte ab, wenn eine ausdrückliche externe " + "Funktions-Rollenzuordnung die Organisationsfunktion mit einer zuweisbaren Mandantenrolle verbindet. Die Funktionszuweisung " + "allein gewährt keine Rechte. Wird die IDM-Zuweisung entfernt, die Organizations-Funktion deaktiviert oder die " + "Access-Zuordnung gelöscht, trägt diese Rollenquelle nicht länger zu den wirksamen Berechtigungen bei. Eine Delegation wirkt " + "unter dem eigenen Konto der delegierten Person. Eine Stellvertretung wirkt erst, nachdem eine interaktive Sitzung genau die " + "aktuelle Zuweisung ausgewählt hat; Access hält reales und vertretenes Konto fest, auditiert Kontextwechsel und verwirft " + "veraltete oder unpassende Auswahlen. Mandantenadministrierende prüfen dies im Dialog zur Erklärung des Benutzerzugriffs: " + "Rollenquellen verweisen auf die definierende Organizations-Funktion oder -Einheit und auf die erzeugende IDM-Zuweisung. " + "Dieselbe Erklärung steht über die Admin-API bereit, während Zuordnungen unter Admin > Funktions-Rollenzuordnungen verwaltet " + "werden. So bleibt die Verantwortung nachvollziehbar: Organizations definiert, was existieren kann, IDM hält fest, wer es " + "innehat, und Access bestimmt, welche bestätigten Merkmale Berechtigungen erzeugen." + ), + } + }, metadata={ "kind": "reference", "help_contexts": [ @@ -1052,8 +1634,15 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( "route": "/admin", "api_path": "/api/v1/admin/external-function-role-mappings", "explanation_api_path": "/api/v1/admin/users/{user_id}/access-explanation", - "runtime_routes": ["/admin?section=tenant-function-role-mappings", "/organizations?section=functions", "/idm"], - "acting_context_api_paths": ["/api/v1/auth/acting-contexts", "/api/v1/auth/switch-acting-context"], + "runtime_routes": [ + "/admin?section=tenant-function-role-mappings", + "/organizations?section=functions", + "/idm", + ], + "acting_context_api_paths": [ + "/api/v1/auth/acting-contexts", + "/api/v1/auth/switch-acting-context", + ], "permission_scopes": ["access:function:write", "access:role:assign"], "responsibility_boundaries": { "organizations": "Defines organization units, structures, function types, and functions.", @@ -1091,8 +1680,16 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = ( ), ), links=( - DocumentationLink(label="Data-subject requests", href="/admin?section=tenant-data-subject-requests", kind="runtime"), - DocumentationLink(label="Data-subject request API", href="/api/v1/admin/privacy/data-subject-requests", kind="api"), + DocumentationLink( + label="Data-subject requests", + href="/admin?section=tenant-data-subject-requests", + kind="runtime", + ), + DocumentationLink( + label="Data-subject request API", + href="/api/v1/admin/privacy/data-subject-requests", + kind="api", + ), ), translations={ "de": { @@ -1209,7 +1806,9 @@ def _optional_idm_directory(context: ModuleContext) -> IdmDirectory | None: return capability -def _optional_organization_directory(context: ModuleContext) -> OrganizationDirectory | None: +def _optional_organization_directory( + context: ModuleContext, +) -> OrganizationDirectory | None: if not context.registry.has_capability(CAPABILITY_ORGANIZATION_DIRECTORY): return None capability = context.registry.require_capability(CAPABILITY_ORGANIZATION_DIRECTORY) @@ -1218,7 +1817,9 @@ def _optional_organization_directory(context: ModuleContext) -> OrganizationDire return capability -def _resource_explanation_providers(context: ModuleContext) -> tuple[ResourceAccessExplanationProvider, ...]: +def _resource_explanation_providers( + context: ModuleContext, +) -> tuple[ResourceAccessExplanationProvider, ...]: providers: list[ResourceAccessExplanationProvider] = [] for capability_name in (CAPABILITY_FILES_ACCESS, CAPABILITY_CAMPAIGNS_ACCESS): if not context.registry.has_capability(capability_name): @@ -1242,7 +1843,9 @@ def _access_explanation_service(context: ModuleContext) -> object: def _tenant_provisioner(context: ModuleContext) -> object: del context - from govoplan_access.backend.tenancy.provisioning import LegacyTenantAccessProvisioner + from govoplan_access.backend.tenancy.provisioning import ( + LegacyTenantAccessProvisioner, + ) return LegacyTenantAccessProvisioner() @@ -1263,14 +1866,18 @@ def _access_administration(context: ModuleContext) -> object: def _governance_materializer(context: ModuleContext) -> object: del context - from govoplan_access.backend.governance_materializer import SqlAccessGovernanceMaterializer + from govoplan_access.backend.governance_materializer import ( + SqlAccessGovernanceMaterializer, + ) return SqlAccessGovernanceMaterializer() def _configuration_provider(context: ModuleContext) -> object: del context - from govoplan_access.backend.configuration_provider import SqlAccessConfigurationProvider + from govoplan_access.backend.configuration_provider import ( + SqlAccessConfigurationProvider, + ) return SqlAccessConfigurationProvider() @@ -1311,7 +1918,7 @@ def _people_search(context: ModuleContext) -> object: manifest = ModuleManifest( id="access", name="Access", - version="0.1.20", + version="0.1.21", optional_dependencies=("identity", "organizations", "tenancy", "idm"), provides_interfaces=( ModuleInterfaceProvider(name=CAPABILITY_ACCESS_PEOPLE_SEARCH, version="0.1.0"), @@ -1357,34 +1964,139 @@ manifest = ModuleManifest( label="Access", ), ), - nav_items=(NavItem(path="/admin", label="Admin", icon="admin", required_any=ADMIN_READ_SCOPES, order=900),), + nav_items=( + NavItem( + path="/admin", + label="Admin", + icon="admin", + required_any=ADMIN_READ_SCOPES, + order=900, + ), + ), frontend=FrontendModule( module_id="access", package_name="@govoplan/access-webui", - routes=(FrontendRoute(path="/admin", component="AdminPage", required_any=ADMIN_READ_SCOPES, order=900),), - nav_items=(NavItem(path="/admin", label="Admin", icon="admin", required_any=ADMIN_READ_SCOPES, order=900),), + routes=( + FrontendRoute( + path="/admin", + component="AdminPage", + required_any=ADMIN_READ_SCOPES, + order=900, + ), + ), + nav_items=( + NavItem( + path="/admin", + label="Admin", + icon="admin", + required_any=ADMIN_READ_SCOPES, + order=900, + ), + ), view_surfaces=( - ViewSurface(id="access.admin.system-roles", module_id="access", kind="section", label="System roles", order=20), - ViewSurface(id="access.admin.system-users", module_id="access", kind="section", label="System users", order=50), - ViewSurface(id="access.admin.system-credentials", module_id="access", kind="section", label="System credentials", order=80), - ViewSurface(id="access.admin.tenant-roles", module_id="access", kind="section", label="Tenant roles", order=10), - ViewSurface(id="access.admin.tenant-function-mappings", module_id="access", kind="section", label="Function mappings", order=20), - ViewSurface(id="access.admin.tenant-groups", module_id="access", kind="section", label="Tenant groups", order=30), - ViewSurface(id="access.admin.tenant-users", module_id="access", kind="section", label="Tenant users", order=40), - ViewSurface(id="access.admin.tenant-credentials", module_id="access", kind="section", label="Tenant credentials", order=70), - ViewSurface(id="access.admin.tenant-api-keys", module_id="access", kind="section", label="Tenant API keys", order=80), - ViewSurface(id="access.admin.tenant-service-accounts", module_id="access", kind="section", label="Service accounts", order=90), - ViewSurface(id="access.admin.group-credentials", module_id="access", kind="section", label="Group credentials", order=30), - ViewSurface(id="access.admin.user-credentials", module_id="access", kind="section", label="User credentials", order=30), - ViewSurface(id="access.settings.credentials", module_id="access", kind="section", label="Personal credentials", order=30), - ViewSurface(id="access.settings.sessions", module_id="access", kind="section", label="Sessions and devices", order=20), + ViewSurface( + id="access.admin.system-roles", + module_id="access", + kind="section", + label="System roles", + order=20, + ), + ViewSurface( + id="access.admin.system-users", + module_id="access", + kind="section", + label="System users", + order=50, + ), + ViewSurface( + id="access.admin.system-credentials", + module_id="access", + kind="section", + label="System credentials", + order=80, + ), + ViewSurface( + id="access.admin.tenant-roles", + module_id="access", + kind="section", + label="Tenant roles", + order=10, + ), + ViewSurface( + id="access.admin.tenant-function-mappings", + module_id="access", + kind="section", + label="Function mappings", + order=20, + ), + ViewSurface( + id="access.admin.tenant-groups", + module_id="access", + kind="section", + label="Tenant groups", + order=30, + ), + ViewSurface( + id="access.admin.tenant-users", + module_id="access", + kind="section", + label="Tenant users", + order=40, + ), + ViewSurface( + id="access.admin.tenant-credentials", + module_id="access", + kind="section", + label="Tenant credentials", + order=70, + ), + ViewSurface( + id="access.admin.tenant-api-keys", + module_id="access", + kind="section", + label="Tenant API keys", + order=80, + ), + ViewSurface( + id="access.admin.tenant-service-accounts", + module_id="access", + kind="section", + label="Service accounts", + order=90, + ), + ViewSurface( + id="access.admin.group-credentials", + module_id="access", + kind="section", + label="Group credentials", + order=30, + ), + ViewSurface( + id="access.admin.user-credentials", + module_id="access", + kind="section", + label="User credentials", + order=30, + ), + ViewSurface( + id="access.settings.credentials", + module_id="access", + kind="section", + label="Personal credentials", + order=30, + ), + ViewSurface( + id="access.settings.sessions", + module_id="access", + kind="section", + label="Sessions and devices", + order=20, + ), ), ), capability_factories={ CAPABILITY_AUTH_API_PRINCIPAL_PROVIDER: _api_principal_provider, - CAPABILITY_AUTH_AUTOMATION_PRINCIPAL_PROVIDER: ( - _automation_principal_provider - ), + CAPABILITY_AUTH_AUTOMATION_PRINCIPAL_PROVIDER: (_automation_principal_provider), CAPABILITY_AUTH_PRINCIPAL_RESOLVER: _legacy_principal_resolver, CAPABILITY_AUTH_PERMISSION_EVALUATOR: _legacy_permission_evaluator, CAPABILITY_AUTH_TENANT_CONTEXT_SWITCHER: _tenant_context_switcher, @@ -1420,9 +2132,21 @@ manifest = ModuleManifest( maturity="vertical_slice", documentation_ref="docs/ACCESS_MODULE_BOUNDARY.md", test_ref="tests/test_login_security.py", - known_limits=("Recovery and upgrade evidence is not yet complete enough for supported maturity.",), - owned_concepts=("account authentication", "application role", "permission evaluation", "service account"), - non_owned_concepts=("person identity", "organization structure", "function incumbency", "policy definition"), + known_limits=( + "Recovery and upgrade evidence is not yet complete enough for supported maturity.", + ), + owned_concepts=( + "account authentication", + "application role", + "permission evaluation", + "service account", + ), + non_owned_concepts=( + "person identity", + "organization structure", + "function incumbency", + "policy definition", + ), security_docs=("docs/ACCESS_MODULE_BOUNDARY.md",), operations_docs=("README.md",), ), diff --git a/tests/test_interface_documentation_contract.py b/tests/test_interface_documentation_contract.py index f05711d..10ca1b3 100644 --- a/tests/test_interface_documentation_contract.py +++ b/tests/test_interface_documentation_contract.py @@ -6,6 +6,18 @@ from govoplan_access.backend.manifest import manifest class InterfaceDocumentationContractTests(unittest.TestCase): + def test_all_static_topics_have_complete_german_content(self) -> None: + for topic in manifest.documentation: + german = (topic.translations or {}).get("de", {}) + self.assertEqual( + {"title", "summary", "body"}, + set(german), + topic.id, + ) + self.assertTrue( + all(str(value).strip() for value in german.values()), topic.id + ) + def test_access_admin_topics_publish_stable_help_contexts(self) -> None: topics = {topic.id: topic for topic in manifest.documentation} @@ -112,9 +124,7 @@ class InterfaceDocumentationContractTests(unittest.TestCase): ) def test_access_admin_surfaces_remain_declared(self) -> None: - surface_ids = { - surface.id for surface in manifest.frontend.view_surfaces - } + surface_ids = {surface.id for surface in manifest.frontend.view_surfaces} self.assertTrue( { "access.admin.system-roles", diff --git a/webui/package.json b/webui/package.json index a3e9e04..616e929 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/access-webui", - "version": "0.1.20", + "version": "0.1.21", "private": true, "type": "module", "scripts": {