- infrasynth.configs: typed multi-tenant config store (registry, service, secrets, cache) + public config_changed/config_reset signals and API - emit the declared-but-dead signals (features flags/overrides, scheduler task_completed/task_failed, tenancy tenant_updated, audit model_changed) and per-model audit field exclusions - security: permission catalog (security_permission), Django-style model-derived AutoPermission, PermissionRegistry, RoleAssignment, global-or-tenant Grant/Revoke, catalog API - consolidate the permission surface: PermissionRegistry only (drop the settings dict), IsAuthenticatedAndPermitted aliases HybridPermission, require_permission replaced by required_permissions + require_all - packaging: add [build-system]; add Forgejo publish workflow (.forgejo)
71 lines
1.9 KiB
Python
71 lines
1.9 KiB
Python
"""Registry of custom permissions declared by apps.
|
|
|
|
Consuming apps (and the kit itself) register codenames in ``apps.py:ready()``
|
|
so they appear in the permission catalog and can be assigned to roles/users
|
|
without editing the kit. Model-derived CRUD/view permissions are generated
|
|
automatically by :mod:`infrasynth.security.catalog` and do not need to be
|
|
registered here.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
__all__ = ["PermissionDefinition", "PermissionRegistry"]
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class PermissionDefinition:
|
|
codename: str
|
|
name: str = ""
|
|
app: str = ""
|
|
model: str = ""
|
|
action: str = ""
|
|
group: str = ""
|
|
description: str = ""
|
|
is_custom: bool = True
|
|
|
|
|
|
class PermissionRegistry:
|
|
"""Global registry of explicitly declared permissions."""
|
|
|
|
_permissions: dict[str, PermissionDefinition] = {}
|
|
|
|
@classmethod
|
|
def register(
|
|
cls,
|
|
codename: str,
|
|
*,
|
|
name: str = "",
|
|
app: str = "",
|
|
model: str = "",
|
|
action: str = "",
|
|
group: str = "",
|
|
description: str = "",
|
|
is_custom: bool = True,
|
|
) -> PermissionDefinition:
|
|
definition = PermissionDefinition(
|
|
codename=codename,
|
|
name=name or codename,
|
|
app=app or codename.split(".", 1)[0],
|
|
model=model,
|
|
action=action,
|
|
group=group or (app or codename.split(".", 1)[0]).replace("_", " ").title(),
|
|
description=description,
|
|
is_custom=is_custom,
|
|
)
|
|
cls._permissions[codename] = definition
|
|
return definition
|
|
|
|
@classmethod
|
|
def get(cls, codename: str) -> PermissionDefinition | None:
|
|
return cls._permissions.get(codename)
|
|
|
|
@classmethod
|
|
def all(cls) -> dict[str, PermissionDefinition]:
|
|
return dict(cls._permissions)
|
|
|
|
@classmethod
|
|
def clear(cls) -> None:
|
|
"""Clears the registry. Tests only."""
|
|
cls._permissions.clear()
|