Use core-owned scope storage
This commit is contained in:
14
README.md
14
README.md
@@ -1,9 +1,11 @@
|
|||||||
# GovOPlaN Tenancy
|
# GovOPlaN Tenancy
|
||||||
|
|
||||||
`govoplan-tenancy` owns the live `tenancy_tenants` table, tenant lifecycle, and
|
`govoplan-tenancy` owns tenant lifecycle, tenant administration API route
|
||||||
tenant settings API route contributions during the GovOPlaN module split.
|
contributions, and the `tenancy.tenantResolver` capability during the GovOPlaN
|
||||||
|
module split.
|
||||||
|
|
||||||
`govoplan-access` depends on this module, and core's registry inserts tenancy
|
`govoplan-access` no longer hard-depends on this module. Access can run in the
|
||||||
before access for authenticated platform composition. Core migrations rename the
|
single-scope compatibility mode used by the core/access baseline; installing
|
||||||
historical `tenants` table to the module-prefixed table name for existing
|
tenancy adds explicit tenant management and resolver behavior. The shared scope
|
||||||
development databases.
|
storage table is core-owned as `core_scopes`; tenancy provides lifecycle and
|
||||||
|
administration behavior over those rows rather than owning the table.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from sqlalchemy.orm import Session
|
|||||||
|
|
||||||
from govoplan_core.admin.common import AdminValidationError, slugify
|
from govoplan_core.admin.common import AdminValidationError, slugify
|
||||||
from govoplan_core.admin.settings import get_system_settings
|
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.audit.logging import audit_event
|
||||||
from govoplan_core.core.access import CAPABILITY_ACCESS_TENANT_PROVISIONER, TenantAccessProvisioner
|
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
|
from govoplan_core.core.change_sequence import ChangeSequenceEntry, decode_sequence_watermark, encode_sequence_watermark, record_change, sequence_watermark_is_expired
|
||||||
|
|||||||
@@ -1,33 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import uuid
|
from govoplan_core.tenancy.scope import Tenant, new_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")
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["Tenant", "new_uuid"]
|
__all__ = ["Tenant", "new_uuid"]
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from govoplan_core.core.access import CAPABILITY_TENANCY_TENANT_RESOLVER
|
from govoplan_core.core.access import (
|
||||||
from govoplan_core.core.module_guards import persistent_table_uninstall_guard
|
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
|
||||||
from govoplan_core.core.modules import MigrationSpec, ModuleContext, ModuleManifest
|
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
|
||||||
from govoplan_core.db.base import Base
|
CAPABILITY_TENANCY_TENANT_RESOLVER,
|
||||||
from govoplan_tenancy.backend.db import models as tenancy_models # noqa: F401 - populate Tenancy ORM metadata
|
)
|
||||||
|
from govoplan_core.core.modules import ModuleContext, ModuleManifest
|
||||||
|
|
||||||
|
|
||||||
def _tenant_resolver(context: ModuleContext):
|
def _tenant_resolver(context: ModuleContext):
|
||||||
@@ -25,11 +26,8 @@ manifest = ModuleManifest(
|
|||||||
id="tenancy",
|
id="tenancy",
|
||||||
name="Tenancy",
|
name="Tenancy",
|
||||||
version="0.1.6",
|
version="0.1.6",
|
||||||
|
required_capabilities=(CAPABILITY_AUTH_PRINCIPAL_RESOLVER, CAPABILITY_AUTH_PERMISSION_EVALUATOR),
|
||||||
route_factory=_route_factory,
|
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_factories={
|
||||||
CAPABILITY_TENANCY_TENANT_RESOLVER: _tenant_resolver,
|
CAPABILITY_TENANCY_TENANT_RESOLVER: _tenant_resolver,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user