From 7e8df56723067a001fe921b6581fafc6d06da76f Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Fri, 10 Jul 2026 17:33:43 +0200 Subject: [PATCH] Use core-owned scope storage --- README.md | 14 +++++---- src/govoplan_tenancy/backend/api/v1/routes.py | 2 +- src/govoplan_tenancy/backend/db/models.py | 29 +------------------ src/govoplan_tenancy/backend/manifest.py | 16 +++++----- 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4dbc2c1..d5f9ac0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # GovOPlaN Tenancy -`govoplan-tenancy` owns the live `tenancy_tenants` table, tenant lifecycle, and -tenant settings API route contributions during the GovOPlaN module split. +`govoplan-tenancy` owns tenant lifecycle, tenant administration API route +contributions, and the `tenancy.tenantResolver` capability during the GovOPlaN +module split. -`govoplan-access` depends on this module, and core's registry inserts tenancy -before access for authenticated platform composition. Core migrations rename the -historical `tenants` table to the module-prefixed table name for existing -development databases. +`govoplan-access` no longer hard-depends on this module. Access can run in the +single-scope compatibility mode used by the core/access baseline; installing +tenancy adds explicit tenant management and resolver behavior. The shared scope +storage table is core-owned as `core_scopes`; tenancy provides lifecycle and +administration behavior over those rows rather than owning the table. diff --git a/src/govoplan_tenancy/backend/api/v1/routes.py b/src/govoplan_tenancy/backend/api/v1/routes.py index 3a3acd3..fd9baf4 100644 --- a/src/govoplan_tenancy/backend/api/v1/routes.py +++ b/src/govoplan_tenancy/backend/api/v1/routes.py @@ -6,7 +6,7 @@ from sqlalchemy.orm import Session from govoplan_core.admin.common import AdminValidationError, slugify from govoplan_core.admin.settings import get_system_settings -from govoplan_access.auth import ApiPrincipal, get_api_principal, has_scope, require_scope +from govoplan_core.auth import ApiPrincipal, get_api_principal, has_scope, require_scope from govoplan_core.audit.logging import audit_event from govoplan_core.core.access import CAPABILITY_ACCESS_TENANT_PROVISIONER, TenantAccessProvisioner from govoplan_core.core.change_sequence import ChangeSequenceEntry, decode_sequence_watermark, encode_sequence_watermark, record_change, sequence_watermark_is_expired diff --git a/src/govoplan_tenancy/backend/db/models.py b/src/govoplan_tenancy/backend/db/models.py index 1dd0005..ed4acbf 100644 --- a/src/govoplan_tenancy/backend/db/models.py +++ b/src/govoplan_tenancy/backend/db/models.py @@ -1,33 +1,6 @@ from __future__ import annotations -import uuid -from typing import Any - -from sqlalchemy import Boolean, JSON, String, Text -from sqlalchemy.orm import Mapped, mapped_column, relationship - -from govoplan_core.db.base import Base, TimestampMixin - - -def new_uuid() -> str: - return str(uuid.uuid4()) - - -class Tenant(Base, TimestampMixin): - __tablename__ = "tenancy_tenants" - - id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) - slug: Mapped[str] = mapped_column(String(100), unique=True, nullable=False, index=True) - name: Mapped[str] = mapped_column(String(255), nullable=False) - description: Mapped[str | None] = mapped_column(Text) - default_locale: Mapped[str] = mapped_column(String(20), default="en", nullable=False) - settings: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False) - allow_custom_groups: Mapped[bool | None] = mapped_column(Boolean, nullable=True) - allow_custom_roles: Mapped[bool | None] = mapped_column(Boolean, nullable=True) - allow_api_keys: Mapped[bool | None] = mapped_column(Boolean, nullable=True) - is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) - - users: Mapped[list["User"]] = relationship("User", back_populates="tenant", cascade="all, delete-orphan") +from govoplan_core.tenancy.scope import Tenant, new_uuid __all__ = ["Tenant", "new_uuid"] diff --git a/src/govoplan_tenancy/backend/manifest.py b/src/govoplan_tenancy/backend/manifest.py index 58b0734..66d8bcf 100644 --- a/src/govoplan_tenancy/backend/manifest.py +++ b/src/govoplan_tenancy/backend/manifest.py @@ -1,10 +1,11 @@ from __future__ import annotations -from govoplan_core.core.access import CAPABILITY_TENANCY_TENANT_RESOLVER -from govoplan_core.core.module_guards import persistent_table_uninstall_guard -from govoplan_core.core.modules import MigrationSpec, ModuleContext, ModuleManifest -from govoplan_core.db.base import Base -from govoplan_tenancy.backend.db import models as tenancy_models # noqa: F401 - populate Tenancy ORM metadata +from govoplan_core.core.access import ( + CAPABILITY_AUTH_PERMISSION_EVALUATOR, + CAPABILITY_AUTH_PRINCIPAL_RESOLVER, + CAPABILITY_TENANCY_TENANT_RESOLVER, +) +from govoplan_core.core.modules import ModuleContext, ModuleManifest def _tenant_resolver(context: ModuleContext): @@ -25,11 +26,8 @@ manifest = ModuleManifest( id="tenancy", name="Tenancy", version="0.1.6", + required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR), route_factory=_route_factory, - migration_spec=MigrationSpec(module_id="tenancy", metadata=Base.metadata), - uninstall_guard_providers=( - persistent_table_uninstall_guard(tenancy_models.Tenant, label="Tenancy"), - ), capability_factories={ CAPABILITY_TENANCY_TENANT_RESOLVER: _tenant_resolver, },