Release v0.1.6

This commit is contained in:
2026-07-07 16:00:38 +02:00
parent a2053518d1
commit 150b720f12
149 changed files with 14311 additions and 8005 deletions

View File

@@ -0,0 +1,70 @@
from __future__ import annotations
from dataclasses import dataclass
from sqlalchemy.orm import Session
from govoplan_core.admin.common import AdminValidationError
from govoplan_core.admin.settings import get_system_settings
from govoplan_core.core.runtime import get_registry
from govoplan_access.backend.db.models import ApiKey, Group, User
from govoplan_tenancy.backend.db.models import Tenant
@dataclass(frozen=True)
class EffectiveTenantGovernance:
allow_custom_groups: bool
allow_custom_roles: bool
allow_api_keys: bool
def _narrowing_bool(system_allows: bool, tenant_override: bool | None) -> bool:
if not system_allows:
return False
return tenant_override is not False
def effective_tenant_governance(session: Session, tenant: Tenant) -> EffectiveTenantGovernance:
defaults = get_system_settings(session)
return EffectiveTenantGovernance(
allow_custom_groups=_narrowing_bool(defaults.allow_tenant_custom_groups, tenant.allow_custom_groups),
allow_custom_roles=_narrowing_bool(defaults.allow_tenant_custom_roles, tenant.allow_custom_roles),
allow_api_keys=_narrowing_bool(defaults.allow_tenant_api_keys, tenant.allow_api_keys),
)
def assert_tenant_governance_override_allowed(session: Session, *, field: str, value: bool | None) -> None:
if value is not True:
return
defaults = get_system_settings(session)
blocked = {
"allow_custom_groups": not defaults.allow_tenant_custom_groups,
"allow_custom_roles": not defaults.allow_tenant_custom_roles,
"allow_api_keys": not defaults.allow_tenant_api_keys,
}
if blocked.get(field):
raise AdminValidationError("Tenant governance cannot explicitly allow a capability denied by system settings.")
def _tenant_module_counts(session: Session, tenant_id: str) -> dict[str, int]:
registry = get_registry()
if registry is None or not hasattr(registry, "tenant_summary_providers"):
return {}
counts: dict[str, int] = {}
for provider in registry.tenant_summary_providers().values():
provided = provider(session, tenant_id)
counts.update({str(key): int(value) for key, value in provided.items()})
return counts
def tenant_counts(session: Session, tenant_id: str) -> dict[str, int]:
module_counts = _tenant_module_counts(session, tenant_id)
return {
"users": session.query(User).filter(User.tenant_id == tenant_id).count(),
"active_users": session.query(User).filter(User.tenant_id == tenant_id, User.is_active.is_(True)).count(),
"groups": session.query(Group).filter(Group.tenant_id == tenant_id).count(),
"campaigns": module_counts.get("campaigns", 0),
"files": module_counts.get("files", 0),
"api_keys": session.query(ApiKey).filter(ApiKey.tenant_id == tenant_id, ApiKey.revoked_at.is_(None)).count(),
}