Coverage for src/lexigram/admin/rbac/inventory.py: 0%

17 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-24 23:18 +0800

1"""Permission inventory for the RBAC admin UI. 

2 

3The form pages render grouped checkboxes from an inventory of 

4``resource.action`` strings. Supply the builtin resources set 

5(``roles``, ``users``, ``settings``) plus any resources discovered from 

6the bundle provider's registered resource classes. The service is 

7mutable and registered as a container singleton so discovery can 

8populate it at mount time; the controller reads ``options()`` on every 

9request, so late registrations appear immediately. 

10""" 

11 

12from __future__ import annotations 

13 

14from collections.abc import Iterable 

15 

16_RBAC_RESOURCES: tuple[str, ...] = ("roles", "users", "settings") 

17_RBAC_ACTIONS: tuple[str, ...] = ( 

18 "list", 

19 "view", 

20 "create", 

21 "update", 

22 "delete", 

23 "export", 

24) 

25 

26 

27class PermissionInventoryService: 

28 """Mutable permission inventory for the RBAC editing pages.""" 

29 

30 def __init__(self) -> None: 

31 """Initialise with the builtin resources only.""" 

32 self._resources: list[str] = list(_RBAC_RESOURCES) 

33 

34 def register_resources(self, names: Iterable[str]) -> None: 

35 """Append unknown, non-blank resource names (case normalized). 

36 

37 Duplicates and blank entries are ignored. 

38 

39 Args: 

40 names: Resource names to add to the inventory. 

41 """ 

42 for name in names: 

43 key = str(name).strip().lower() 

44 if key and key not in self._resources: 

45 self._resources.append(key) 

46 

47 def resources(self) -> tuple[str, ...]: 

48 """Return the current resource names, builtin first.""" 

49 return tuple(self._resources) 

50 

51 def options(self) -> dict[str, list[str]]: 

52 """Return grouped permission options for every resource. 

53 

54 Returns: 

55 Mapping ``{resource: ["resource.action", ...]}`` with all 

56 builtin actions per resource. 

57 """ 

58 return { 

59 resource: [f"{resource}.{action}" for action in _RBAC_ACTIONS] 

60 for resource in self._resources 

61 } 

62 

63 

64__all__ = [ 

65 "_RBAC_ACTIONS", 

66 "_RBAC_RESOURCES", 

67 "PermissionInventoryService", 

68]