diff --git a/package.json b/package.json index 0a818c5..3be8fab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/idm-webui", - "version": "0.1.18", + "version": "0.1.22", "private": true, "type": "module", "main": "webui/src/index.ts", diff --git a/pyproject.toml b/pyproject.toml index b96211a..a882091 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-idm" -version = "0.1.21" +version = "0.1.22" description = "GovOPlaN identity management bridge module." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_idm/backend/manifest.py b/src/govoplan_idm/backend/manifest.py index 3b657cb..7862f9e 100644 --- a/src/govoplan_idm/backend/manifest.py +++ b/src/govoplan_idm/backend/manifest.py @@ -66,7 +66,7 @@ from govoplan_idm.backend.search_source import create_idm_search_source from govoplan_idm.backend.scim import SCIM_EXTERNAL_PROVIDER_ID -MODULE_VERSION = "0.1.21" +MODULE_VERSION = "0.1.22" IDM_READ_SCOPES = ( "idm:organization_assignment:read", @@ -204,7 +204,9 @@ def _idm_directory(context: ModuleContext) -> object: from govoplan_idm.backend.directory import SqlIdmDirectory identities = context.registry.require_capability(CAPABILITY_IDENTITY_DIRECTORY) - organizations = context.registry.require_capability(CAPABILITY_ORGANIZATION_DIRECTORY) + organizations = context.registry.require_capability( + CAPABILITY_ORGANIZATION_DIRECTORY + ) if not isinstance(identities, IdentityDirectory): raise RuntimeError(f"Invalid capability: {CAPABILITY_IDENTITY_DIRECTORY}") if not isinstance(organizations, OrganizationDirectory): @@ -376,12 +378,32 @@ manifest = ModuleManifest( factory=create_idm_search_source, ), ), - nav_items=(NavItem(path="/idm", label="IDM", icon="users", required_any=IDM_READ_SCOPES, order=72),), + nav_items=( + NavItem( + path="/idm", + label="IDM", + icon="users", + required_any=IDM_READ_SCOPES, + order=72, + ), + ), frontend=FrontendModule( module_id="idm", package_name="@govoplan/idm-webui", - routes=(FrontendRoute(path="/idm", component="IdmPage", required_any=IDM_READ_SCOPES, order=72),), - nav_items=(NavItem(path="/idm", label="IDM", icon="users", required_any=IDM_READ_SCOPES, order=72),), + routes=( + FrontendRoute( + path="/idm", component="IdmPage", required_any=IDM_READ_SCOPES, order=72 + ), + ), + nav_items=( + NavItem( + path="/idm", + label="IDM", + icon="users", + required_any=IDM_READ_SCOPES, + order=72, + ), + ), product_areas=( ProductAreaContribution( id="people-responsibility", @@ -548,9 +570,7 @@ manifest = ModuleManifest( "organizations", "records", ), - conditions=( - DocumentationCondition(required_modules=("idm", "access")), - ), + conditions=(DocumentationCondition(required_modules=("idm", "access")),), links=( DocumentationLink( label="Data-subject requests", @@ -563,6 +583,28 @@ manifest = ModuleManifest( kind="runtime", ), ), + translations={ + "de": { + "title": "IDM-Daten in einer Betroffenenanfrage prüfen", + "summary": ( + "Mandantenbezogene institutionelle Funktions- und Beziehungsmerkmale erfassen und dabei gesteuerte " + "Entscheidungsnachweise bewahren." + ), + "body": ( + "IDM durchsucht bestätigte Konto- und Identitätsselektoren sowie namensraumgebundene Zuweisungs-, Beziehungs- und " + "Zuweisungsänderungsverweise. Ergebnisse umfassen zeitlich wirksame Organisationsfunktionszuweisungen, typisierte " + "Identitätsbeziehungen mit minimiertem Gruppenkontext, gesteuerte Zuweisungsanträge oder -erteilungen und zugehörige " + "Lebenszyklusereignisse. Betrifft ein Datensatz eine andere kandidierende oder handelnde Person, werden deren Identitäts- " + "und Kontokennungen aus dem automatischen Export entfernt. Einstellungen, Gruppeneigenschaften, externe Quellverweise, " + "Herkunft, Begründungen, Nachweislisten, Richtlinienentscheidungen, Workflow-Interna, Idempotenzschlüssel, Anfrage-Digests, " + "undurchsichtige Metadaten, Ereigniskommentare und -details, unbeteiligte Datensätze und andere Mandanten sind ausgeschlossen. " + "Zuweisungen und Beziehungen sind wirksame institutionelle Tatsachen; Korrektur, Widerruf, Deaktivierung oder Ablauf " + "erfordern daher eine berechtigte IDM-Lebenszyklusprüfung. Gesteuerte Änderungs- und Ereignisdatensätze bewahren " + "ausdrückliche Gründe für Entscheidungsnachweise. Identity besitzt den Personendatensatz, Organizations Funktionen und " + "Einheiten und Access die aus bestätigten IDM-Tatsachen abgeleitete Befugnis." + ), + } + }, metadata={ "kind": "guide", "help_contexts": [ @@ -587,6 +629,21 @@ manifest = ModuleManifest( documentation_types=("admin", "user"), audience=("tenant_admin", "access_admin", "operator"), related_modules=("search", "identity", "organizations"), + translations={ + "de": { + "title": "Berechtigte IDM-Datensätze durchsuchen", + "summary": ( + "Typisierte Gruppen, zeitlich wirksame Beziehungen und Organisationsfunktionszuweisungen für die " + "berechtigungsbewusste Plattform-Suche bereitstellen." + ), + "body": ( + "Ist Search installiert, liefert IDM begrenzte Verzeichnis- und Zuweisungsmetadaten, ohne uneingeschränkte " + "Herkunftsdaten zu kopieren. Jedes Ergebnis bleibt mandantengebunden und prüft die aktuelle Leseberechtigung für " + "Zuweisung oder Beziehung erneut. Festgeschriebene IDM-Lebenszyklusereignisse aktualisieren den abgeleiteten Index; ein " + "betrieblicher Neuaufbau gleicht Datensätze ab, die vor der Aktivierung von Search angelegt wurden." + ), + } + }, order=25, ), DocumentationTopic( @@ -602,6 +659,20 @@ manifest = ModuleManifest( documentation_types=("admin", "user"), audience=("tenant_admin", "access_admin", "operator"), related_modules=("identity", "organizations", "access"), + translations={ + "de": { + "title": "Brücke zwischen Identität und Organisation", + "summary": ( + "IDM löst auf, welche Identitäten und Konten Organisationsfunktionszuweisungen zugeordnet werden können." + ), + "body": ( + "Identity besitzt normalisierte Identitäten und Kontoverknüpfungen. Organizations besitzt Einheiten und Funktionen. " + "IDM besitzt die Zuweisungsverknüpfungen zwischen Identitäten und Organisationsfunktionen, einschließlich Identitätssuche " + "für Organisationszuweisungen und zukünftiger Synchronisations-/Zuordnungsabläufe. Access darf diese Verknüpfungen nutzen, " + "wenn IDM installiert ist; IDM benötigt Access jedoch nicht, um Rechte auszuwerten." + ), + } + }, order=26, ), DocumentationTopic( @@ -631,6 +702,26 @@ manifest = ModuleManifest( kind="api", ), ), + translations={ + "de": { + "title": "Governance von IDM-Zuweisungen", + "summary": ( + "Mandantenbezogene IDM-Einstellungen können freigegebene Änderungsanträge verlangen, bevor " + "Identitäts-Funktionszuweisungen angewendet werden." + ), + "body": ( + "Zuweisungsverknüpfungen haben hohe Auswirkung, weil sie später Zugriffsentscheidungen speisen können. Mandanten können " + "aufgezeichnete Änderungsanträge für Anlage und Aktualisierung von Zuweisungen verlangen. Zusätzlich lassen sich " + "Obergrenzen für Tiefe und Gültigkeit von Delegationsketten sowie ausdrückliche Eskalationsziele und Fristen für Prüfungen " + "durch Inhabende, verantwortliche Stelle oder Empfangende konfigurieren. IDM prüft vollständige Wege bei jeder " + "Entscheidung und der endgültigen Anwendung gegen die aktuelle Policy. Abgelaufene Fristen werden als sichtbarer " + "eskalierter Zustand mit Notifications- und Auditnachweis erfasst; eine Freigabe wird nicht unterstellt. Ein periodischer " + "Worker sendet genau ein Ablaufereignis, wenn eine zukünftige Zuweisung endet. Kennzeichnung und Ereignis werden gemeinsam " + "festgeschrieben, damit Wiederholungen idempotent bleiben. Der alte Bereich organizations:function:assign bleibt während " + "des Übergangs gültig; neue Rollenvorlagen sollten idm:organization_assignment:write gewähren." + ), + } + }, metadata={ "kind": "reference", "help_contexts": [ @@ -672,10 +763,24 @@ manifest = ModuleManifest( ), ), links=( - DocumentationLink(label="IDM relationship administration", href="/idm", kind="runtime"), - DocumentationLink(label="Typed groups API", href="/api/v1/idm/typed-groups", kind="api"), - DocumentationLink(label="Identity relationships API", href="/api/v1/idm/relationships", kind="api"), - DocumentationLink(label="Typed relationship contract", href="docs/TYPED_RELATIONSHIPS.md", kind="repository"), + DocumentationLink( + label="IDM relationship administration", href="/idm", kind="runtime" + ), + DocumentationLink( + label="Typed groups API", + href="/api/v1/idm/typed-groups", + kind="api", + ), + DocumentationLink( + label="Identity relationships API", + href="/api/v1/idm/relationships", + kind="api", + ), + DocumentationLink( + label="Typed relationship contract", + href="docs/TYPED_RELATIONSHIPS.md", + kind="repository", + ), ), translations={ "de": { @@ -779,9 +884,7 @@ manifest = ModuleManifest( documentation_types=("admin", "user"), audience=("tenant_admin", "access_admin", "operator", "user"), related_modules=("identity", "organizations", "access"), - conditions=( - DocumentationCondition(any_scopes=IDM_READ_SCOPES), - ), + conditions=(DocumentationCondition(any_scopes=IDM_READ_SCOPES),), links=( DocumentationLink( label="IDM assignments", @@ -794,6 +897,24 @@ manifest = ModuleManifest( kind="api", ), ), + translations={ + "de": { + "title": "Einer Identität eine Organisationsfunktion zuweisen", + "summary": ( + "IDM verknüpft Identitäten oder Konten mit Organisationsfunktionen; Access kann bestätigte Verknüpfungen bei " + "vorhandener Funktions-Rollenzuordnung verwenden." + ), + "body": ( + "Legen Sie zuerst Einheit und Funktion in Organizations an und stellen Sie sicher, dass Person und Konto in Identity " + "bestehen. Erstellen Sie anschließend die Zuweisung in IDM. Direkte Zuweisungen halten fest, wer die Funktion innehat. " + "Delegierte Zuweisungen benötigen eine Quellzuweisung und eine delegierbare Funktion. Stellvertretungszuweisungen benötigen " + "Quellzuweisung, handelndes Konto und eine Funktion, die Handeln an Stelle zulässt. Access bildet bestätigte " + "Funktionsmerkmale auf Rollen und Rechte ab; ohne eine solche Zuordnung wird die Zuweisung gespeichert, gewährt aber keine " + "Anwendungsberechtigungen. Der Zuweisungsarbeitsbereich nutzt die verfügbare Anwendungsbreite, sodass Governance-Steuerungen " + "und Zuweisungsdaten gemeinsam sichtbar bleiben." + ), + } + }, metadata={ "kind": "workflow", "help_contexts": [ @@ -835,7 +956,14 @@ manifest = ModuleManifest( layer="configured", documentation_types=("admin", "user"), audience=("tenant_admin", "access_admin", "operator", "user"), - related_modules=("identity", "organizations", "access", "policy", "audit", "workflow_engine"), + related_modules=( + "identity", + "organizations", + "access", + "policy", + "audit", + "workflow_engine", + ), links=( DocumentationLink( label="Function assignment workflows", @@ -843,6 +971,30 @@ manifest = ModuleManifest( kind="repository", ), ), + translations={ + "de": { + "title": "Felder und Folgen von IDM-Zuweisungen", + "summary": ( + "Referenz für direkte Zuweisungen, Delegation, Stellvertretung, Wirksamkeitsdaten, gesteuerte Änderungen, Nachweise und " + "Aufbewahrung." + ), + "body": ( + "Identität und Konto wählen, wer die institutionelle Tatsache erhält; Funktion und Einheit gehören Organizations. Die " + "Quelle unterscheidet direkte, delegierte, stellvertretende, Verzeichnis-, Governance- und Systemtatsachen. Delegation und " + "Stellvertretung verlangen eine gültige Quellzuweisung und die entsprechende Organizations-Funktionsberechtigung. Eine " + "delegierte Person handelt als sie selbst; bei Stellvertretung muss Access zusätzlich den exakten Repräsentationskontext " + "auswählen, bevor daraus Befugnis entsteht. Quell- und abgeleitete Zuweisungen müssen aktuell, aktiv, mandantenlokal und " + "funktionskompatibel bleiben. Der Untereinheitenbereich erweitert die organisatorische Reichweite. Deaktivierung und Ablauf " + "bewahren die Herkunft, entfernen die Zuweisung aber aus der wirksamen Auflösung. Entscheidungen zu gesteuerten Anträgen " + "und Erteilungen bewahren handelnde Person, Richtlinie, Workflow-Revision, Kommentare und Nachweise. Delegierte Befugnis " + "wird über die vollständige Quellkette gegen aktuelle Tiefen- und Gültigkeitsgrenzen geprüft. Eine konfigurierte Frist pro " + "Schritt erzeugt einen sichtbaren eskalierten Zustand und eine exakte Zielfunktionsroute; sie ersetzt oder protokolliert " + "niemals automatisch eine freigebende Person. Eine Notfallüberschreibung ist nicht der Normalweg und benötigt einen " + "ausdrücklichen Grund. Eine IDM-Zuweisung allein gewährt niemals Anwendungsberechtigungen; Access verlangt eine " + "ausdrückliche Zuordnung." + ), + } + }, metadata={ "kind": "reference", "help_contexts": [ @@ -878,12 +1030,34 @@ manifest = ModuleManifest( maturity="vertical_slice", documentation_ref="docs/FUNCTION_ASSIGNMENT_WORKFLOWS.md", test_ref="tests/test_assignment_workflow.py", - known_limits=("SCIM provisioning is a deterministic preview; governed plan execution is not implemented yet.",), + known_limits=( + "SCIM provisioning is a deterministic preview; governed plan execution is not implemented yet.", + ), supported_authority_modes=("external_authoritative", "external_mirror"), - owned_concepts=("function assignment", "assignment delegation", "acting-for assignment", "assignment request", "typed group", "identity relationship"), - non_owned_concepts=("identity", "organization function", "application role", "workflow runtime"), - recovery_docs=("docs/FUNCTION_ASSIGNMENT_WORKFLOWS.md", "docs/TYPED_RELATIONSHIPS.md", "docs/SCIM_PROVISIONING.md"), - security_docs=("docs/FUNCTION_ASSIGNMENT_WORKFLOWS.md", "docs/TYPED_RELATIONSHIPS.md", "docs/SCIM_PROVISIONING.md"), + owned_concepts=( + "function assignment", + "assignment delegation", + "acting-for assignment", + "assignment request", + "typed group", + "identity relationship", + ), + non_owned_concepts=( + "identity", + "organization function", + "application role", + "workflow runtime", + ), + recovery_docs=( + "docs/FUNCTION_ASSIGNMENT_WORKFLOWS.md", + "docs/TYPED_RELATIONSHIPS.md", + "docs/SCIM_PROVISIONING.md", + ), + security_docs=( + "docs/FUNCTION_ASSIGNMENT_WORKFLOWS.md", + "docs/TYPED_RELATIONSHIPS.md", + "docs/SCIM_PROVISIONING.md", + ), operations_docs=("README.md", "docs/SCIM_PROVISIONING.md"), ), ) diff --git a/tests/test_interface_documentation_contract.py b/tests/test_interface_documentation_contract.py index d7e4784..7d26ecf 100644 --- a/tests/test_interface_documentation_contract.py +++ b/tests/test_interface_documentation_contract.py @@ -7,6 +7,14 @@ from govoplan_idm.backend.api.v1.routes import ORGANIZATION_IDENTITY_READ_SCOPES class IdmInterfaceDocumentationContractTests(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_route_and_contributed_action_remain_declared(self) -> None: frontend = manifest.frontend self.assertIsNotNone(frontend) @@ -54,8 +62,13 @@ class IdmInterfaceDocumentationContractTests(unittest.TestCase): "idm.typed-groups.action.resolve-memberships", relationships.metadata["help_contexts"], ) - self.assertIn("Revocation immediately", relationships.metadata["consequences"][2]) - self.assertIn("Access permissions remain unchanged", relationships.metadata["verification"]) + self.assertIn( + "Revocation immediately", relationships.metadata["consequences"][2] + ) + self.assertIn( + "Access permissions remain unchanged", + relationships.metadata["verification"], + ) def test_relationship_writers_may_use_identity_search_selectors(self) -> None: self.assertIn("idm:relationship:write", ORGANIZATION_IDENTITY_READ_SCOPES) diff --git a/webui/package.json b/webui/package.json index 05352b7..4f9f5de 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,6 +1,6 @@ { "name": "@govoplan/idm-webui", - "version": "0.1.21", + "version": "0.1.22", "private": true, "type": "module", "main": "src/index.ts",