Release v0.1.5

This commit is contained in:
2026-07-07 15:49:06 +02:00
parent f37c6668e5
commit efb69b3d2d
43 changed files with 6464 additions and 192 deletions

View File

@@ -0,0 +1,82 @@
from __future__ import annotations
from collections.abc import Mapping
from sqlalchemy.orm import Session
from govoplan_access.backend.admin.service import ensure_default_roles
from govoplan_access.backend.db.models import Account, User, UserRoleAssignment
from govoplan_core.admin.common import AdminValidationError
from govoplan_core.core.access import TenantAccessProvisioner, TenantOwnerCandidateRef
class LegacyTenantAccessProvisioner(TenantAccessProvisioner):
def ensure_default_roles(self, session: object, tenant: object | None = None) -> Mapping[str, object]:
return ensure_default_roles(session, tenant) # type: ignore[arg-type]
def tenant_owner_candidates(self, session: object) -> tuple[TenantOwnerCandidateRef, ...]:
db = _session(session)
accounts = db.query(Account).filter(Account.is_active.is_(True)).order_by(Account.email.asc()).all()
return tuple(
TenantOwnerCandidateRef(account_id=account.id, email=account.email, display_name=account.display_name)
for account in accounts
)
def ensure_tenant_owner_membership(
self,
session: object,
*,
tenant: object,
owner_account_id: str,
) -> str:
db = _session(session)
tenant_id = getattr(tenant, "id", None)
if not tenant_id:
raise AdminValidationError("Tenant owner membership requires a persisted tenant.")
roles = ensure_default_roles(db, tenant) # type: ignore[arg-type]
owner_role = roles.get("owner")
if owner_role is None:
raise AdminValidationError("Tenant owner role is not available.")
owner_account = db.get(Account, owner_account_id)
if owner_account is None or not owner_account.is_active:
raise AdminValidationError("The selected tenant owner account is missing or inactive.")
membership = (
db.query(User)
.filter(User.tenant_id == tenant_id, User.account_id == owner_account.id)
.one_or_none()
)
if membership is None:
membership = User(
tenant_id=tenant_id,
account_id=owner_account.id,
email=owner_account.email,
display_name=owner_account.display_name,
is_active=True,
auth_provider=owner_account.auth_provider,
password_hash=owner_account.password_hash,
)
db.add(membership)
db.flush()
existing_assignment = (
db.query(UserRoleAssignment)
.filter(
UserRoleAssignment.tenant_id == tenant_id,
UserRoleAssignment.user_id == membership.id,
UserRoleAssignment.role_id == owner_role.id,
)
.one_or_none()
)
if existing_assignment is None:
db.add(UserRoleAssignment(tenant_id=tenant_id, user_id=membership.id, role_id=owner_role.id))
db.flush()
return membership.id
def _session(session: object) -> Session:
if not isinstance(session, Session):
raise TypeError("Tenant access provisioner requires a SQLAlchemy Session")
return session