404 lines
16 KiB
Python
404 lines
16 KiB
Python
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.module_guards import persistent_table_uninstall_guard
|
|
from govoplan_core.core.modules import (
|
|
DocumentationLink,
|
|
DocumentationTopic,
|
|
FrontendModule,
|
|
FrontendRoute,
|
|
MigrationSpec,
|
|
ModuleContext,
|
|
ModuleInterfaceProvider,
|
|
ModuleManifest,
|
|
NavItem,
|
|
PermissionDefinition,
|
|
ProductAreaContribution,
|
|
RoleTemplate,
|
|
)
|
|
from govoplan_core.core.provider_governance import declared_module_architecture
|
|
from govoplan_core.core.organizations import (
|
|
CAPABILITY_ORGANIZATION_DIRECTORY,
|
|
CAPABILITY_ORGANIZATION_HIERARCHY_DIRECTORY,
|
|
)
|
|
from govoplan_core.core.views import ViewSurface
|
|
from govoplan_core.db.base import Base
|
|
from govoplan_organizations.backend.db import models as organization_models # noqa: F401 - populate metadata
|
|
|
|
|
|
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:settings:read",
|
|
"View organization settings",
|
|
"Read organization governance, audit, and retention settings.",
|
|
),
|
|
_permission(
|
|
"organizations:settings:write",
|
|
"Manage organization settings",
|
|
"Edit organization governance, audit, and retention settings.",
|
|
),
|
|
_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.",
|
|
),
|
|
_permission(
|
|
"organizations:function:write",
|
|
"Manage organization functions",
|
|
"Create and edit function definitions.",
|
|
),
|
|
)
|
|
|
|
ROLE_TEMPLATES = (
|
|
RoleTemplate(
|
|
slug="organization_modeler",
|
|
name="Organization modeler",
|
|
description="Manage organization meta-model, concrete units, structures, and functions.",
|
|
permissions=tuple(permission.scope for permission in PERMISSIONS),
|
|
),
|
|
RoleTemplate(
|
|
slug="organization_viewer",
|
|
name="Organization viewer",
|
|
description="Read organization model, organization units, and functions.",
|
|
permissions=(
|
|
"organizations:model:read",
|
|
"organizations:settings: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:
|
|
del context
|
|
from govoplan_organizations.backend.directory import SqlOrganizationDirectory
|
|
|
|
return SqlOrganizationDirectory()
|
|
|
|
|
|
manifest = ModuleManifest(
|
|
id="organizations",
|
|
name="Organizations",
|
|
version="0.1.18",
|
|
required_capabilities=(
|
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
|
),
|
|
optional_dependencies=("tenancy", "access", "audit", "policy"),
|
|
provides_interfaces=(
|
|
ModuleInterfaceProvider(
|
|
name="organizations.directory",
|
|
version="0.1.0",
|
|
),
|
|
ModuleInterfaceProvider(
|
|
name="organizations.hierarchy_directory",
|
|
version="0.1.0",
|
|
),
|
|
),
|
|
permissions=PERMISSIONS,
|
|
role_templates=ROLE_TEMPLATES,
|
|
route_factory=_route_factory,
|
|
nav_items=(
|
|
NavItem(
|
|
path="/organizations",
|
|
label="Organizations",
|
|
icon="building-2",
|
|
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="building-2",
|
|
required_any=ORGANIZATIONS_READ_SCOPES,
|
|
order=70,
|
|
),
|
|
),
|
|
product_areas=(
|
|
ProductAreaContribution(
|
|
id="people-responsibility",
|
|
module_id="organizations",
|
|
label="i18n:govoplan-core.product_area.people_responsibility",
|
|
icon="users",
|
|
description="i18n:govoplan-core.product_area.people_responsibility_description",
|
|
surface_ids=("organizations.nav.organizations", "organizations.route.organizations"),
|
|
order=70,
|
|
),
|
|
),
|
|
view_surfaces=(
|
|
ViewSurface(
|
|
id="organizations.admin.tenant",
|
|
module_id="organizations",
|
|
kind="section",
|
|
label="Organizations administration",
|
|
order=85,
|
|
),
|
|
ViewSurface(
|
|
id="organizations.admin.template-upgrades",
|
|
module_id="organizations",
|
|
kind="section",
|
|
label="Organization template upgrades",
|
|
parent_id="organizations.admin.tenant",
|
|
order=86,
|
|
),
|
|
),
|
|
),
|
|
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.OrganizationTenantSettings,
|
|
organization_models.OrganizationModelTemplate,
|
|
organization_models.OrganizationModelTemplateVersion,
|
|
organization_models.OrganizationModelInstantiation,
|
|
organization_models.OrganizationModelUpgrade,
|
|
organization_models.OrganizationStructure,
|
|
organization_models.OrganizationRelationType,
|
|
organization_models.OrganizationRelation,
|
|
organization_models.OrganizationUnit,
|
|
organization_models.OrganizationFunctionType,
|
|
organization_models.OrganizationFunction,
|
|
label="Organizations",
|
|
),
|
|
),
|
|
capability_factories={
|
|
CAPABILITY_ORGANIZATION_DIRECTORY: _organization_directory,
|
|
CAPABILITY_ORGANIZATION_HIERARCHY_DIRECTORY: (_organization_directory),
|
|
},
|
|
documentation=(
|
|
DocumentationTopic(
|
|
id="organizations.template-upgrades",
|
|
title="Upgrade a tenant organization model",
|
|
summary=(
|
|
"Compare an immutable system template version with the current "
|
|
"tenant-owned model before explicitly applying an upgrade."
|
|
),
|
|
body=(
|
|
"Open Organizations administration and create a preview for a newer "
|
|
"published version of the template used by the tenant. The persisted "
|
|
"three-way comparison separates compatible additions, compatible "
|
|
"changes, tenant-only divergence, destructive remapping, and invalid "
|
|
"references. Tenant-only changes are preserved. Conflicting or "
|
|
"destructive entries require an explicit keep, replace, or bounded "
|
|
"mapping decision. Applying the reviewed preview creates a new "
|
|
"tenant-owned instantiation and supersedes the previous provenance; "
|
|
"it never creates live inheritance. A stale preview is rejected if the "
|
|
"tenant model or either template version changed. Cancelling retains "
|
|
"the review record without modifying organization data."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin",),
|
|
audience=("tenant_admin", "operator"),
|
|
related_modules=("policy", "audit", "idm", "access"),
|
|
links=(
|
|
DocumentationLink(
|
|
label="Organizations administration",
|
|
href="/admin?section=tenant-organization-settings",
|
|
kind="runtime",
|
|
),
|
|
DocumentationLink(
|
|
label="Organization upgrade API",
|
|
href="/api/v1/organizations/model-upgrades",
|
|
kind="api",
|
|
),
|
|
),
|
|
metadata={
|
|
"kind": "guide",
|
|
"help_contexts": [
|
|
"organizations.admin.template-upgrades",
|
|
"organizations.template-upgrade.preview",
|
|
"organizations.template-upgrade.decision",
|
|
"organizations.template-upgrade.apply",
|
|
],
|
|
},
|
|
order=27,
|
|
),
|
|
DocumentationTopic(
|
|
id="organizations.model",
|
|
title="Organization model",
|
|
summary="Organizations owns units, hierarchy, and functions. IDM links identities to functions, and Access maps accepted facts to roles and rights.",
|
|
body=(
|
|
"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 describe responsibilities in organization units. IDM links identities to those functions, and Access maps accepted facts to roles and rights. "
|
|
"A function does not itself prove mandate, jurisdiction, decision authority, or signature authority; those effective institutional facts belong to a separate provider contract."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin", "user"),
|
|
audience=("tenant_admin", "access_admin", "operator"),
|
|
related_modules=("tenancy", "access", "idm", "policy", "audit"),
|
|
links=(
|
|
DocumentationLink(
|
|
label="Organizations workspace",
|
|
href="/organizations",
|
|
kind="runtime",
|
|
),
|
|
DocumentationLink(
|
|
label="Organization model API",
|
|
href="/api/v1/organizations/model",
|
|
kind="api",
|
|
),
|
|
),
|
|
metadata={
|
|
"kind": "guide",
|
|
"help_contexts": [
|
|
"organizations.workspace",
|
|
"organizations.model",
|
|
"organizations.units",
|
|
"organizations.relations",
|
|
"organizations.functions",
|
|
"organizations.admin.tenant",
|
|
"organizations.blocked",
|
|
],
|
|
},
|
|
order=25,
|
|
),
|
|
DocumentationTopic(
|
|
id="organizations.reference.fields-and-consequences",
|
|
title="Organization model fields and consequences",
|
|
summary=(
|
|
"Distinguish tenant-owned model definitions, concrete units, "
|
|
"relations, functions, governance references, and lifecycle state."
|
|
),
|
|
body=(
|
|
"Unit types, structures, relation types, and function types define "
|
|
"the tenant-owned model. Units, relations, and functions are concrete "
|
|
"institutional facts within that model. Slugs are stable references for "
|
|
"integrations; parent and relation changes affect hierarchy traversal and "
|
|
"downstream routing. Deactivation retains the record and evidence but "
|
|
"removes it from active selection. Delegation and act-in-place flags only "
|
|
"describe permitted organizational semantics; Access and governed workflows "
|
|
"still decide effective authority. When configured, a recorded change-request "
|
|
"ID is required before model mutations. Organization settings are tenant-owned "
|
|
"and do not inherit a live global hierarchy."
|
|
),
|
|
layer="configured",
|
|
documentation_types=("admin",),
|
|
audience=("tenant_admin", "access_admin", "operator"),
|
|
related_modules=("tenancy", "access", "idm", "policy", "audit"),
|
|
links=(
|
|
DocumentationLink(
|
|
label="Organizations workspace",
|
|
href="/organizations",
|
|
kind="runtime",
|
|
),
|
|
DocumentationLink(
|
|
label="Organization settings API",
|
|
href="/api/v1/organizations/settings",
|
|
kind="api",
|
|
),
|
|
),
|
|
metadata={
|
|
"kind": "reference",
|
|
"help_contexts": [
|
|
"organizations.field.name",
|
|
"organizations.field.slug",
|
|
"organizations.field.parent",
|
|
"organizations.field.relation",
|
|
"organizations.field.function",
|
|
"organizations.field.change-request",
|
|
"organizations.field.audit-retention",
|
|
"organizations.action.deactivate",
|
|
],
|
|
"consequence_classes": {
|
|
"model_change": "Changes the valid vocabulary and constraints for tenant-owned organization facts.",
|
|
"hierarchy_change": "Changes traversal, routing, and inherited institutional context for downstream modules.",
|
|
"deactivate": "Retains the fact and evidence while removing it from active selection.",
|
|
"settings": "Changes tenant-owned governance, audit detail, and retention behavior.",
|
|
},
|
|
},
|
|
order=26,
|
|
),
|
|
),
|
|
architecture=declared_module_architecture(
|
|
layer="institutional_foundation",
|
|
kind="foundation",
|
|
maturity="vertical_slice",
|
|
documentation_ref="docs/ORGANIZATION_MODEL.md",
|
|
test_ref="tests/test_model_templates.py",
|
|
known_limits=(
|
|
"Downstream modules must reconcile organization-reference changes from the emitted upgrade event; cross-module records are not rewritten directly.",
|
|
),
|
|
owned_concepts=("organization unit", "organization structure", "organization relation", "organization function"),
|
|
non_owned_concepts=("function incumbency", "identity", "application role", "mandate"),
|
|
recovery_docs=("docs/ORGANIZATION_MODEL.md",),
|
|
security_docs=("docs/ORGANIZATION_MODEL.md",),
|
|
),
|
|
)
|
|
|
|
|
|
def get_manifest() -> ModuleManifest:
|
|
return manifest
|