"""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()