Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-skills/src/lexigram/ai/skills/permissions/permission_checker.py: 41%

27 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 07:19 +0800

1"""PermissionChecker — verifies a user has the required permissions to run a skill.""" 

2 

3from __future__ import annotations 

4 

5from lexigram.logging import ( 

6 get_logger, 

7) 

8 

9logger = get_logger(__name__) 

10 

11 

12class PermissionChecker: 

13 """Grant and verify per-user permission sets for skill execution. 

14 

15 Permissions are plain strings (e.g. ``"files.read"``, ``"db.query"``). 

16 A user with no registered permissions is treated as having *no* permissions. 

17 

18 Example:: 

19 

20 checker = PermissionChecker() 

21 checker.grant("user-123", {"files.read", "web.search"}) 

22 allowed = checker.check("user-123", {"files.read"}) # True 

23 denied = checker.check("user-123", {"db.write"}) # False 

24 """ 

25 

26 def __init__(self) -> None: 

27 """Initialise an empty permission store.""" 

28 self._permissions: dict[str, set[str]] = {} 

29 

30 def grant(self, user_id: str, permissions: set[str]) -> None: 

31 """Add permissions for a user. 

32 

33 Args: 

34 user_id: The user identifier. 

35 permissions: Set of permission strings to grant. 

36 """ 

37 if user_id not in self._permissions: 

38 self._permissions[user_id] = set() 

39 self._permissions[user_id].update(permissions) 

40 logger.debug("permissions_granted", user_id=user_id, count=len(permissions)) 

41 

42 def revoke(self, user_id: str, permissions: set[str]) -> None: 

43 """Remove permissions for a user. 

44 

45 Args: 

46 user_id: The user identifier. 

47 permissions: Set of permission strings to revoke. 

48 """ 

49 if user_id in self._permissions: 

50 self._permissions[user_id].difference_update(permissions) 

51 

52 def set_permissions(self, user_id: str, permissions: set[str]) -> None: 

53 """Replace all permissions for a user with the given set. 

54 

55 Args: 

56 user_id: The user identifier. 

57 permissions: Complete replacement permission set. 

58 """ 

59 self._permissions[user_id] = set(permissions) 

60 

61 def get_permissions(self, user_id: str) -> set[str]: 

62 """Return the current permission set for a user. 

63 

64 Args: 

65 user_id: The user identifier. 

66 

67 Returns: 

68 Set of permission strings (may be empty). 

69 """ 

70 return set(self._permissions.get(user_id, set())) 

71 

72 def check(self, user_id: str, required: set[str]) -> bool: 

73 """Check whether a user possesses *all* required permissions. 

74 

75 Args: 

76 user_id: The user to check. 

77 required: Permissions that must all be present. 

78 

79 Returns: 

80 ``True`` if every required permission is granted; ``False`` 

81 otherwise (including when *required* is empty — returns ``True``). 

82 """ 

83 if not required: 

84 return True 

85 user_perms = self._permissions.get(user_id, set()) 

86 allowed = required.issubset(user_perms) 

87 if not allowed: 

88 missing = required - user_perms 

89 logger.debug( 

90 "permission_check_denied", 

91 user_id=user_id, 

92 missing=sorted(missing), 

93 ) 

94 return allowed