Move access permissions to access module

This commit is contained in:
2026-07-10 23:27:48 +02:00
parent a7c486788e
commit e32841077c
13 changed files with 1319 additions and 170 deletions

View File

@@ -19,6 +19,7 @@ from govoplan_core.core.access import (
CAPABILITY_AUTH_TENANT_CONTEXT_SWITCHER,
)
from govoplan_core.core.idm import CAPABILITY_IDM_DIRECTORY, IdmDirectory
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.modules import (
DocumentationCondition,
@@ -54,9 +55,18 @@ 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: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"),
@@ -75,6 +85,8 @@ ACCESS_PERMISSIONS: tuple[PermissionDefinition, ...] = (
_permission("access:api_key:revoke", "Revoke API keys", "Revoke tenant API keys.", "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: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: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"),
)
@@ -97,18 +109,50 @@ ACCESS_ROLE_TEMPLATES: tuple[RoleTemplate, ...] = (
"access:tenant:read",
"access:tenant:create",
"access:tenant:update",
"access:tenant:suspend",
"access:account:read",
"access:account:create",
"access:account:update",
"access:account:suspend",
"access:system_role:read",
"access:system_role:write",
"access:system_role:assign",
"access:system_setting:read",
"access:system_setting:write",
"access:governance:read",
"access:governance:write",
),
level="system",
managed=True,
managed=False,
protected=False,
),
RoleTemplate(
slug="tenant_owner",
slug="system_auditor",
name="System auditor",
description="Read tenant registry, accounts, system roles, settings, governance, and cross-tenant audit records.",
permissions=(
"access:tenant:read",
"access:account:read",
"access:system_role:read",
"access:audit:read",
"access:system_setting:read",
"access:governance:read",
),
level="system",
managed=False,
protected=False,
),
RoleTemplate(
slug="maintenance_operator",
name="Maintenance operator",
description="Access the system while maintenance mode is active.",
permissions=("access:system_setting:read", "access:maintenance:access"),
level="system",
managed=False,
protected=False,
),
RoleTemplate(
slug="owner",
name="Tenant owner",
description="Protected full tenant administration and module access.",
permissions=("tenant:*",),
@@ -116,6 +160,41 @@ ACCESS_ROLE_TEMPLATES: tuple[RoleTemplate, ...] = (
managed=True,
protected=True,
),
RoleTemplate(
slug="tenant_admin",
name="Tenant administrator",
description="Manage tenant settings, policies, users, groups, roles and API keys.",
permissions=(
"access:membership:read",
"access:membership:create",
"access:membership:update",
"access:group:read",
"access:group:write",
"access:group:manage_members",
"access:role:read",
"access:role:write",
"access:role:assign",
"access:api_key:read",
"access:api_key:create",
"access:api_key:revoke",
"access:setting:read",
"access:setting:write",
"access:policy:read",
"access:policy:write",
),
level="tenant",
managed=True,
protected=False,
),
RoleTemplate(
slug="admin",
name="Administrator (legacy)",
description="Legacy broad tenant role retained for upgraded installations.",
permissions=("tenant:*",),
level="tenant",
managed=True,
protected=False,
),
RoleTemplate(
slug="access_admin",
name="Access administrator",
@@ -323,14 +402,14 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = (
),
DocumentationTopic(
id="access.reference.external-function-role-mappings",
title="Function facts and access roles",
summary="Access maps accepted organization function facts to roles and permissions. IDM assignment ownership remains separate from authorization.",
title="Organization function facts and access roles",
summary="Organizations defines function facts, IDM assigns them to identities, and Access maps accepted facts to roles and permissions.",
body=(
"When IDM is installed, Access can consume identity-to-organization-function assignments through the IDM directory capability. "
"Organizations must be installed because Access validates mappings against the Organizations function directory instead of accepting arbitrary function identifiers. "
"An assignment does not grant rights by itself. Access grants rights only when an explicit external function role mapping connects the organization function ID to an assignable tenant role. "
"Tenant administrators manage these mappings under Admin > Function role mappings, and the same records are exposed through the admin API. "
"This keeps the audit trail clear: IDM records who holds a function, while Access records which function facts produce role-derived permissions."
"Organizations owns the organization meta-model, concrete units, structures, and functions. IDM owns the fact that an identity, through one of its accounts, holds a function in an organization unit, including delegated and acting-for assignments. "
"Access consumes those accepted IDM facts through the directory capability, validates function identifiers against Organizations, and turns them into rights only when an explicit external function role mapping connects the organization function ID to an assignable tenant role. "
"The assignment itself does not grant rights. Removing the IDM assignment, disabling the Organizations function, or removing the Access mapping stops the derived role source from contributing effective permissions. "
"Tenant administrators inspect this from the user access explanation dialog: role sources link back to the Organizations function or unit that defines the fact and to the IDM assignment that produced it. "
"The same explanation is exposed by the admin API, while mapping management remains under Admin > Function role mappings. This keeps the audit trail clear: Organizations records what can exist, IDM records who holds it, and Access records which accepted facts produce permissions."
),
layer="configured",
documentation_types=("admin", "user"),
@@ -345,13 +424,22 @@ ACCESS_DOCUMENTATION: tuple[DocumentationTopic, ...] = (
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="IDM assignments", href="/idm", kind="runtime"),
),
metadata={
"kind": "reference",
"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"],
"permission_scopes": ["access:function:write", "access:role:assign"],
"responsibility_boundaries": {
"organizations": "Defines organization units, structures, function types, and functions.",
"idm": "Assigns organization functions to identities and accounts, including delegation and acting-for facts.",
"access": "Maps accepted function facts to tenant roles and explains effective permissions.",
},
"related_topic_ids": [
"idm.workflow.assign-function-to-identity",
"docs.reference.organization-identity-idm-access-boundary",
@@ -410,11 +498,22 @@ def _optional_idm_directory(context: ModuleContext) -> IdmDirectory | None:
return capability
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)
if not isinstance(capability, OrganizationDirectory):
raise RuntimeError(f"Invalid capability: {CAPABILITY_ORGANIZATION_DIRECTORY}")
return capability
def _access_explanation_service(context: ModuleContext) -> object:
del context
from govoplan_access.backend.explanation import SqlAccessExplanationService
return SqlAccessExplanationService()
return SqlAccessExplanationService(
idm_directory=_optional_idm_directory(context),
organization_directory=_optional_organization_directory(context),
)
def _tenant_provisioner(context: ModuleContext) -> object: