Coverage for src/pypermission/models.py: 99%
60 statements
« prev ^ index » next coverage.py v7.11.3, created at 2025-12-01 18:06 +0000
« prev ^ index » next coverage.py v7.11.3, created at 2025-12-01 18:06 +0000
1from typing import Never
3from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
4from sqlalchemy.sql.schema import ForeignKey
5from sqlalchemy.sql.sqltypes import String
7from pypermission.util.input_validation import validate_rbac_parameters
10class PyPermissionORM(DeclarativeBase): ... 10 ↛ 18line 10 didn't jump to line 18 because
13################################################################################
14#### Types
15################################################################################
18class Permission:
19 """
20 Represents a **Resource** paired with an **Action**.
22 Attributes
23 ----------
24 resource_type : str
25 The **ResourceType** (e.g., "document", "user").
26 resource_id : str
27 The **ResourceID**. The star '*' acts as a wildcard matching all **ResourceIDs** of the same **ResourceType**. The empty string can be used for **Actions** on **Resources** that do not have an **ResourceID**.
28 action : str
29 The **Action** allowed on the **Resource** (e.g., "read", "write", "delete").
30 """
32 resource_type: str
33 resource_id: str
34 action: str
36 @validate_rbac_parameters
37 def __init__(self, *, resource_type: str, resource_id: str, action: str) -> None:
38 """
39 Initialize the **Permission**.
41 Parameters
42 ----------
43 resource_type : str
44 The type of the resource (e.g., "document", "user").
45 resource_id : str
46 The ID of the resource instance. The start '*' acts as a wildcard matching all IDs of the resource. The empty string can be used for actions on resources that do not have an ID.
47 action : str
48 The action allowed on the resource (e.g., "read", "write", "delete").
49 """
51 self.resource_type = resource_type
52 self.resource_id = resource_id
53 self.action = action
55 def __str__(self) -> str:
56 if not self.resource_id:
57 return f"{self.resource_type}:{self.action}"
58 return f"{self.resource_type}[{self.resource_id}]:{self.action}"
60 def __eq__(self, other: object) -> bool:
61 if not isinstance(other, Permission):
62 return False
64 return (
65 self.resource_type == other.resource_type
66 and self.resource_id == other.resource_id
67 and self.action == other.action
68 )
70 def __ne__(self, other: object) -> bool:
71 return not self.__eq__(other)
74class Policy:
75 """
76 Represents a **Role** paired with a **Permission**.
78 Attributes
79 ----------
80 role : str
81 The target **RoleID**.
82 permission : Permission
83 The target **Permission**.
84 """
86 role: str
87 permission: Permission
89 @validate_rbac_parameters
90 def __init__(self, *, role: str, permission: Permission) -> None:
91 """
92 Initialize the Policy.
94 Parameters
95 ----------
96 role : str
97 The target **RoleID**.
98 permission : Permission
99 The target **Permission**.
100 """
101 self.role = role
102 self.permission = permission
104 def __str__(self) -> str:
105 return f"{self.role}:{self.permission}"
107 def __eq__(self, other: object) -> bool:
108 if not isinstance(other, Policy):
109 return False
111 return self.role == other.role and self.permission == other.permission
113 def __ne__(self, other: object) -> bool:
114 return not self.__eq__(other)
117class FrozenClass(type):
118 def __setattr__(cls, key: str, value: Never) -> None:
119 if key in cls.__dict__:
120 raise AttributeError("Frozen attributes cannot be modified!")
121 super().__setattr__(key, value)
124################################################################################
125#### RoleORM
126################################################################################
129class RoleORM(PyPermissionORM):
130 __tablename__ = "pp_role_table"
131 id: Mapped[str] = mapped_column(String, primary_key=True)
134################################################################################
135#### HierarchyORM
136################################################################################
139class HierarchyORM(PyPermissionORM):
140 __tablename__ = "pp_hierarchy_table"
141 parent_role_id: Mapped[str] = mapped_column(
142 String, ForeignKey("pp_role_table.id", ondelete="CASCADE"), primary_key=True
143 )
144 child_role_id: Mapped[str] = mapped_column(
145 String, ForeignKey("pp_role_table.id", ondelete="CASCADE"), primary_key=True
146 )
149################################################################################
150#### SubjectORM
151################################################################################
154class SubjectORM(PyPermissionORM):
155 __tablename__ = "pp_subject_table"
156 id: Mapped[str] = mapped_column(String, primary_key=True)
159################################################################################
160#### MemberORM
161################################################################################
164class MemberORM(PyPermissionORM):
165 __tablename__ = "pp_member_table"
166 role_id: Mapped[str] = mapped_column(
167 String, ForeignKey("pp_role_table.id", ondelete="CASCADE"), primary_key=True
168 )
169 subject_id: Mapped[str] = mapped_column(
170 String, ForeignKey("pp_subject_table.id", ondelete="CASCADE"), primary_key=True
171 )
174################################################################################
175#### PolicyORM
176################################################################################
179class PolicyORM(PyPermissionORM):
180 __tablename__ = "pp_policy_table"
181 role_id: Mapped[str] = mapped_column(
182 String, ForeignKey("pp_role_table.id", ondelete="CASCADE"), primary_key=True
183 )
184 resource_type: Mapped[str] = mapped_column(String, primary_key=True)
185 resource_id: Mapped[str] = mapped_column(String, primary_key=True)
186 action: Mapped[str] = mapped_column(String, primary_key=True)