Add organizations module surface

This commit is contained in:
2026-07-10 17:33:53 +02:00
parent 34133ba9cd
commit ad2561b50f
22 changed files with 3284 additions and 35 deletions

View File

@@ -1,28 +1,122 @@
from __future__ import annotations
from pathlib import Path
from govoplan_core.core.access import CAPABILITY_AUTH_PERMISSION_EVALUATOR, CAPABILITY_AUTH_PRINCIPAL_RESOLVER
from govoplan_core.core.identity import CAPABILITY_IDENTITY_DIRECTORY, IdentityDirectory
from govoplan_core.core.module_guards import persistent_table_uninstall_guard
from govoplan_core.core.modules import DocumentationTopic, MigrationSpec, ModuleContext, ModuleManifest
from govoplan_core.core.modules import (
DocumentationTopic,
FrontendModule,
FrontendRoute,
MigrationSpec,
ModuleContext,
ModuleManifest,
NavItem,
PermissionDefinition,
RoleTemplate,
)
from govoplan_core.core.organizations import CAPABILITY_ORGANIZATION_DIRECTORY
from govoplan_core.db.base import Base
from govoplan_organizations.backend.db import models as organization_models # noqa: F401 - populate metadata
def _organization_directory(context: ModuleContext) -> object:
ORGANIZATIONS_READ_SCOPES = (
"organizations:model:read",
"organizations:unit:read",
"organizations:function:read",
"admin:settings:read",
)
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
module_id, resource, action = scope.split(":", 2)
return PermissionDefinition(
scope=scope,
label=label,
description=description,
category="Organizations",
level="tenant",
module_id=module_id,
resource=resource,
action=action,
)
PERMISSIONS = (
_permission("organizations:model:read", "View organization model", "Read organization meta-model definitions such as unit types, structures, and relation types."),
_permission("organizations:model:write", "Manage organization model", "Create and edit organization meta-model definitions."),
_permission("organizations:unit:read", "View organization units", "Read concrete organization units and relations."),
_permission("organizations:unit:write", "Manage organization units", "Create and edit concrete organization units and relations."),
_permission("organizations:function:read", "View organization functions", "Read function definitions and identity-held assignments."),
_permission("organizations:function:write", "Manage organization functions", "Create and edit function definitions."),
_permission("organizations:function:assign", "Assign organization functions", "Assign identity-held functions in organization units."),
)
ROLE_TEMPLATES = (
RoleTemplate(
slug="organization_modeler",
name="Organization modeler",
description="Manage organization meta-model, concrete units, structures, functions, and assignments.",
permissions=tuple(permission.scope for permission in PERMISSIONS),
),
RoleTemplate(
slug="organization_viewer",
name="Organization viewer",
description="Read organization model, organization units, and function assignments.",
permissions=("organizations:model:read", "organizations:unit:read", "organizations:function:read"),
),
)
def _route_factory(context: ModuleContext):
del context
from govoplan_organizations.backend.api.v1.routes import router
return router
def _organization_directory(context: ModuleContext) -> object:
identity_directory: IdentityDirectory | None = None
registry = context.registry
if hasattr(registry, "has_capability") and registry.has_capability(CAPABILITY_IDENTITY_DIRECTORY):
capability = registry.require_capability(CAPABILITY_IDENTITY_DIRECTORY)
if isinstance(capability, IdentityDirectory):
identity_directory = capability
from govoplan_organizations.backend.directory import SqlOrganizationDirectory
return SqlOrganizationDirectory()
return SqlOrganizationDirectory(identity_directory=identity_directory)
manifest = ModuleManifest(
id="organizations",
name="Organizations",
version="0.1.6",
dependencies=("tenancy",),
migration_spec=MigrationSpec(module_id="organizations", metadata=Base.metadata),
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
optional_dependencies=("tenancy", "identity", "access", "audit", "policy"),
permissions=PERMISSIONS,
role_templates=ROLE_TEMPLATES,
route_factory=_route_factory,
nav_items=(NavItem(path="/organizations", label="Organizations", icon="users", required_any=ORGANIZATIONS_READ_SCOPES, order=70),),
frontend=FrontendModule(
module_id="organizations",
package_name="@govoplan/organizations-webui",
routes=(FrontendRoute(path="/organizations", component="OrganizationsPage", required_any=ORGANIZATIONS_READ_SCOPES, order=70),),
nav_items=(NavItem(path="/organizations", label="Organizations", icon="users", required_any=ORGANIZATIONS_READ_SCOPES, order=70),),
),
migration_spec=MigrationSpec(
module_id="organizations",
metadata=Base.metadata,
script_location=str(Path(__file__).with_name("migrations") / "versions"),
),
uninstall_guard_providers=(
persistent_table_uninstall_guard(
organization_models.OrganizationUnitType,
organization_models.OrganizationStructure,
organization_models.OrganizationRelationType,
organization_models.OrganizationRelation,
organization_models.OrganizationUnit,
organization_models.OrganizationFunctionType,
organization_models.OrganizationFunction,
organization_models.OrganizationFunctionAssignment,
label="Organizations",
@@ -37,8 +131,9 @@ manifest = ModuleManifest(
title="Organization model",
summary="Organizations owns units, hierarchy, functions, and function assignments. Access maps those facts to roles and rights.",
body=(
"Use organization functions to model responsibilities that outlive people. "
"A function assignment can apply to one organization unit or to that unit and all subunits."
"Use organization unit types, structures, and relation types to model how the institution describes itself. "
"A concrete organization unit can participate in several structures at the same time, such as an employer hierarchy and an academic structure. "
"Functions are held by identities in organization units. Accounts only exercise those functions through identity and access policy."
),
layer="configured",
documentation_types=("admin", "user"),