abs-auth-rbac-core 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of abs-auth-rbac-core might be problematic. Click here for more details.
- abs_auth_rbac_core/__init__.py +0 -0
- abs_auth_rbac_core/auth/__init__.py +3 -0
- abs_auth_rbac_core/auth/auth_functions.py +31 -0
- abs_auth_rbac_core/auth/jwt_functions.py +134 -0
- abs_auth_rbac_core/auth/middleware.py +50 -0
- abs_auth_rbac_core/models/__init__.py +7 -0
- abs_auth_rbac_core/models/base_model.py +20 -0
- abs_auth_rbac_core/models/gov_casbin_rule.py +25 -0
- abs_auth_rbac_core/models/permissions.py +26 -0
- abs_auth_rbac_core/models/rbac_model.py +10 -0
- abs_auth_rbac_core/models/role_permission.py +12 -0
- abs_auth_rbac_core/models/roles.py +21 -0
- abs_auth_rbac_core/models/seeder/permission_seeder.py +101 -0
- abs_auth_rbac_core/models/user.py +27 -0
- abs_auth_rbac_core/models/user_role.py +20 -0
- abs_auth_rbac_core/rbac/__init__.py +2 -0
- abs_auth_rbac_core/rbac/database.py +52 -0
- abs_auth_rbac_core/rbac/decorator.py +48 -0
- abs_auth_rbac_core/rbac/policy.conf +14 -0
- abs_auth_rbac_core/rbac/service.py +688 -0
- abs_auth_rbac_core/util/__init__.py +0 -0
- abs_auth_rbac_core/util/permission_constants.py +1624 -0
- abs_auth_rbac_core-0.1.0.dist-info/METADATA +232 -0
- abs_auth_rbac_core-0.1.0.dist-info/RECORD +25 -0
- abs_auth_rbac_core-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
from typing import List, Optional, Callable,Any
|
|
2
|
+
import os
|
|
3
|
+
import casbin
|
|
4
|
+
from casbin_sqlalchemy_adapter import Adapter
|
|
5
|
+
from sqlalchemy import and_, select
|
|
6
|
+
from sqlalchemy.orm import Session, joinedload
|
|
7
|
+
|
|
8
|
+
from ..models import (
|
|
9
|
+
Role,
|
|
10
|
+
RolePermission,
|
|
11
|
+
UserRole,
|
|
12
|
+
Users,
|
|
13
|
+
Permission
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from abs_exception_core.exceptions import (
|
|
17
|
+
DuplicatedError,
|
|
18
|
+
NotFoundError,
|
|
19
|
+
PermissionDeniedError
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from ..models.gov_casbin_rule import GovCasbinRule
|
|
23
|
+
|
|
24
|
+
class RBACService:
|
|
25
|
+
def __init__(self, session: Callable[...,Session]):
|
|
26
|
+
"""
|
|
27
|
+
Service For Managing the RBAC
|
|
28
|
+
Args:
|
|
29
|
+
session: Callable[...,Session] -> Session of the SQLAlchemy database engine
|
|
30
|
+
"""
|
|
31
|
+
self.db = session
|
|
32
|
+
self.enforcer = None
|
|
33
|
+
self._initialize_casbin()
|
|
34
|
+
|
|
35
|
+
def _initialize_casbin(self):
|
|
36
|
+
"""
|
|
37
|
+
Initiates the casbin policy using the default rules
|
|
38
|
+
"""
|
|
39
|
+
with self.db() as session:
|
|
40
|
+
engine = session.get_bind()
|
|
41
|
+
|
|
42
|
+
# Create the Casbin rule table if it doesn't exist
|
|
43
|
+
adapter = Adapter(engine,db_class=GovCasbinRule)
|
|
44
|
+
|
|
45
|
+
# Get the directory of the current file
|
|
46
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
47
|
+
# Construct the path to the policy file
|
|
48
|
+
policy_path = os.path.join(current_dir, "policy.conf")
|
|
49
|
+
|
|
50
|
+
self.enforcer = casbin.Enforcer(
|
|
51
|
+
policy_path, adapter
|
|
52
|
+
)
|
|
53
|
+
# Load policies
|
|
54
|
+
self.enforcer.load_policy()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def list_roles(self) -> Any:
|
|
58
|
+
"""
|
|
59
|
+
Get the list of all roles
|
|
60
|
+
"""
|
|
61
|
+
with self.db() as session:
|
|
62
|
+
"""List all roles"""
|
|
63
|
+
total = session.query(Role).count()
|
|
64
|
+
roles = session.query(Role).all()
|
|
65
|
+
return {"roles": roles, "total": total}
|
|
66
|
+
|
|
67
|
+
def create_role(
|
|
68
|
+
self,
|
|
69
|
+
name: str,
|
|
70
|
+
description: Optional[str] = None,
|
|
71
|
+
permission_ids: List[str] = None,
|
|
72
|
+
) -> Any:
|
|
73
|
+
"""
|
|
74
|
+
Create role with the provided permissions
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
name: Name of the role
|
|
78
|
+
description: Optional description of the role
|
|
79
|
+
permission_ids: Optional list of permission UUIDs to assign to the role
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The created role object
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
DuplicatedError: If a role with the same name already exists
|
|
86
|
+
NotFoundError: If any of the provided permission IDs don't exist
|
|
87
|
+
"""
|
|
88
|
+
with self.db() as session:
|
|
89
|
+
try:
|
|
90
|
+
# Check if role with same name already exists
|
|
91
|
+
existing_role = session.query(Role).filter(Role.name == name).first()
|
|
92
|
+
if existing_role:
|
|
93
|
+
raise DuplicatedError(detail="Role already exists")
|
|
94
|
+
|
|
95
|
+
# Create the role
|
|
96
|
+
role = Role(name=name, description=description)
|
|
97
|
+
session.add(role)
|
|
98
|
+
session.flush() # Get the role UUID without committing
|
|
99
|
+
|
|
100
|
+
# If permission IDs are provided, assign them to the role
|
|
101
|
+
if permission_ids:
|
|
102
|
+
# Verify all permissions exist in a single query
|
|
103
|
+
permission_count = (
|
|
104
|
+
session.query(Permission)
|
|
105
|
+
.filter(Permission.uuid.in_(permission_ids))
|
|
106
|
+
.count()
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Check if all permissions were found
|
|
110
|
+
if permission_count != len(permission_ids):
|
|
111
|
+
# Find which permissions are missing
|
|
112
|
+
existing_permissions = (
|
|
113
|
+
session.query(Permission)
|
|
114
|
+
.filter(Permission.uuid.in_(permission_ids))
|
|
115
|
+
.all()
|
|
116
|
+
)
|
|
117
|
+
found_permission_ids = {p.uuid for p in existing_permissions}
|
|
118
|
+
missing_ids = set(permission_ids) - found_permission_ids
|
|
119
|
+
raise NotFoundError(
|
|
120
|
+
detail=f"Permissions with UUIDs '{', '.join(missing_ids)}' not found"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# Get all permissions for Casbin policy creation
|
|
124
|
+
existing_permissions = (
|
|
125
|
+
session.query(Permission)
|
|
126
|
+
.filter(Permission.uuid.in_(permission_ids))
|
|
127
|
+
.all()
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Bulk create role permissions using bulk_insert_mappings for better performance
|
|
131
|
+
role_permissions = [
|
|
132
|
+
{"role_uuid": role.uuid, "permission_uuid": permission_uuid}
|
|
133
|
+
for permission_uuid in permission_ids
|
|
134
|
+
]
|
|
135
|
+
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
136
|
+
|
|
137
|
+
# Batch add Casbin policies
|
|
138
|
+
policies = [
|
|
139
|
+
(role.name, permission.resource, permission.action, permission.module)
|
|
140
|
+
for permission in existing_permissions
|
|
141
|
+
]
|
|
142
|
+
self.enforcer.add_policies(policies)
|
|
143
|
+
self.enforcer.save_policy()
|
|
144
|
+
|
|
145
|
+
# Commit transaction
|
|
146
|
+
session.commit()
|
|
147
|
+
session.refresh(role)
|
|
148
|
+
return role
|
|
149
|
+
|
|
150
|
+
except Exception as e:
|
|
151
|
+
raise e
|
|
152
|
+
|
|
153
|
+
def get_role_with_permissions(self, role_uuid: str) -> Any:
|
|
154
|
+
"""Get role details including its permissions"""
|
|
155
|
+
with self.db() as session:
|
|
156
|
+
# Use joinedload to eagerly load permissions
|
|
157
|
+
role = (
|
|
158
|
+
session.query(Role)
|
|
159
|
+
.options(joinedload(Role.permissions))
|
|
160
|
+
.filter(Role.uuid == role_uuid)
|
|
161
|
+
.first()
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if not role:
|
|
165
|
+
raise NotFoundError(detail="Requested role does not exist")
|
|
166
|
+
|
|
167
|
+
return role
|
|
168
|
+
|
|
169
|
+
def update_role_permissions(
|
|
170
|
+
self,
|
|
171
|
+
role_uuid: str,
|
|
172
|
+
permissions: Optional[List[str]] = None,
|
|
173
|
+
name: Optional[str] = None,
|
|
174
|
+
description: Optional[str] = None,
|
|
175
|
+
) -> Any:
|
|
176
|
+
"""Update role permissions by replacing all existing permissions with new ones"""
|
|
177
|
+
with self.db() as session:
|
|
178
|
+
try:
|
|
179
|
+
if not session.is_active:
|
|
180
|
+
session.begin()
|
|
181
|
+
|
|
182
|
+
# Get role with eager loading of permissions
|
|
183
|
+
role = (
|
|
184
|
+
session.query(Role)
|
|
185
|
+
.options(joinedload(Role.permissions))
|
|
186
|
+
.filter(Role.uuid == role_uuid)
|
|
187
|
+
.first()
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
if not role:
|
|
191
|
+
raise NotFoundError(detail="Requested role does not exist")
|
|
192
|
+
|
|
193
|
+
# Update role information if provided
|
|
194
|
+
if name is not None or description is not None:
|
|
195
|
+
if name:
|
|
196
|
+
# Check if new name already exists for a different role
|
|
197
|
+
existing_role = (
|
|
198
|
+
session.query(Role)
|
|
199
|
+
.filter(Role.name == name, Role.uuid != role_uuid)
|
|
200
|
+
.first()
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if existing_role:
|
|
204
|
+
raise DuplicatedError(detail="Role already exists")
|
|
205
|
+
|
|
206
|
+
if role.name != "super_admin":
|
|
207
|
+
role.name = name
|
|
208
|
+
|
|
209
|
+
if description is not None:
|
|
210
|
+
role.description = description
|
|
211
|
+
|
|
212
|
+
if permissions is not None:
|
|
213
|
+
existing_permissions = role.permissions
|
|
214
|
+
|
|
215
|
+
# Remove Casbin policies for existing permissions
|
|
216
|
+
for existing_permission in existing_permissions:
|
|
217
|
+
self.enforcer.remove_policy(
|
|
218
|
+
role.name,
|
|
219
|
+
existing_permission.resource,
|
|
220
|
+
existing_permission.action,
|
|
221
|
+
existing_permission.module
|
|
222
|
+
)
|
|
223
|
+
self.enforcer.save_policy()
|
|
224
|
+
|
|
225
|
+
# Delete existing role permissions
|
|
226
|
+
session.query(RolePermission).filter(
|
|
227
|
+
RolePermission.role_uuid == role_uuid
|
|
228
|
+
).delete(synchronize_session=False)
|
|
229
|
+
|
|
230
|
+
if permissions:
|
|
231
|
+
# Fetch all permissions in a single query
|
|
232
|
+
permissions_objs = (
|
|
233
|
+
session.query(Permission)
|
|
234
|
+
.filter(Permission.uuid.in_(permissions))
|
|
235
|
+
.all()
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
found_permission_ids = {p.uuid for p in permissions_objs}
|
|
239
|
+
missing_permission_ids = set(permissions) - found_permission_ids
|
|
240
|
+
if missing_permission_ids:
|
|
241
|
+
raise NotFoundError(
|
|
242
|
+
detail=f"Permissions with UUIDs '{', '.join(missing_permission_ids)}' not found"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Bulk insert role permissions
|
|
246
|
+
role_permissions = [
|
|
247
|
+
{"role_uuid": role_uuid, "permission_uuid": permission.uuid}
|
|
248
|
+
for permission in permissions_objs
|
|
249
|
+
]
|
|
250
|
+
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
251
|
+
|
|
252
|
+
# Add Casbin policies
|
|
253
|
+
for permission in permissions_objs:
|
|
254
|
+
self.enforcer.add_policy(
|
|
255
|
+
role.name, permission.resource, permission.action, permission.module
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
self.enforcer.save_policy()
|
|
259
|
+
|
|
260
|
+
session.commit()
|
|
261
|
+
|
|
262
|
+
# Refresh the role to get the updated permissions
|
|
263
|
+
session.refresh(role)
|
|
264
|
+
|
|
265
|
+
# Return the updated role with permissions
|
|
266
|
+
return role
|
|
267
|
+
|
|
268
|
+
except Exception as e:
|
|
269
|
+
raise e
|
|
270
|
+
|
|
271
|
+
def delete_role(self, role_uuid: str,exception_roles:List[str]=None):
|
|
272
|
+
"""Delete a role and its associated permissions"""
|
|
273
|
+
with self.db() as session:
|
|
274
|
+
try:
|
|
275
|
+
if not session.is_active:
|
|
276
|
+
session.begin()
|
|
277
|
+
|
|
278
|
+
role = self.get_role(role_uuid,session)
|
|
279
|
+
|
|
280
|
+
if exception_roles and len(exception_roles) > 0 and role.name in exception_roles:
|
|
281
|
+
raise PermissionDeniedError(detail="You are not allowed to delete the requested role.")
|
|
282
|
+
|
|
283
|
+
# Get role name for Casbin policy removal
|
|
284
|
+
role_name = role.name
|
|
285
|
+
|
|
286
|
+
# Delete role permissions
|
|
287
|
+
role_permissions = (
|
|
288
|
+
session.query(RolePermission)
|
|
289
|
+
.filter(RolePermission.role_uuid == role_uuid)
|
|
290
|
+
.all()
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Remove Casbin policies for each permission
|
|
294
|
+
remove_policies =[]
|
|
295
|
+
for role_permission in role_permissions:
|
|
296
|
+
permission = (
|
|
297
|
+
session.query(Permission)
|
|
298
|
+
.filter(Permission.uuid == role_permission.permission_uuid)
|
|
299
|
+
.first()
|
|
300
|
+
)
|
|
301
|
+
if permission:
|
|
302
|
+
remove_policies.append(
|
|
303
|
+
(role_name, permission.resource, permission.action, permission.module)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
self.enforcer.remove_policies(remove_policies)
|
|
307
|
+
self.enforcer.save_policy()
|
|
308
|
+
|
|
309
|
+
# Delete role permissions
|
|
310
|
+
session.query(RolePermission).filter(
|
|
311
|
+
RolePermission.role_uuid == role_uuid
|
|
312
|
+
).delete()
|
|
313
|
+
|
|
314
|
+
# Delete user role assignments
|
|
315
|
+
session.query(UserRole).filter(UserRole.role_uuid == role_uuid).delete()
|
|
316
|
+
|
|
317
|
+
# Delete role
|
|
318
|
+
session.delete(role)
|
|
319
|
+
session.commit()
|
|
320
|
+
|
|
321
|
+
except Exception as e:
|
|
322
|
+
raise e
|
|
323
|
+
|
|
324
|
+
def list_permissions(self) -> List[Any]:
|
|
325
|
+
"""Get all permissions with their resources and actions"""
|
|
326
|
+
with self.db() as session:
|
|
327
|
+
return session.query(Permission).all()
|
|
328
|
+
|
|
329
|
+
def list_module_permissions(self,module:str) -> List[Any]:
|
|
330
|
+
"""Get all permissions for a module"""
|
|
331
|
+
with self.db() as session:
|
|
332
|
+
return session.query(Permission).filter(Permission.module == module).all()
|
|
333
|
+
|
|
334
|
+
def get_user_permissions(self, user_uuid: str) -> List[Any]:
|
|
335
|
+
"""Get all allowed permissions for a user"""
|
|
336
|
+
with self.db() as session:
|
|
337
|
+
# Get user roles with eager loading of roles and their permissions
|
|
338
|
+
user_roles = (
|
|
339
|
+
session.query(UserRole)
|
|
340
|
+
.join(Role, UserRole.role_uuid == Role.uuid)
|
|
341
|
+
.options(
|
|
342
|
+
joinedload(UserRole.role).joinedload(Role.permissions)
|
|
343
|
+
)
|
|
344
|
+
.filter(UserRole.user_uuid == user_uuid)
|
|
345
|
+
.all()
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
if not user_roles:
|
|
349
|
+
return []
|
|
350
|
+
|
|
351
|
+
# Build response directly from the eagerly loaded data
|
|
352
|
+
result = []
|
|
353
|
+
for user_role in user_roles:
|
|
354
|
+
role = user_role.role
|
|
355
|
+
for permission in role.permissions:
|
|
356
|
+
result.append(
|
|
357
|
+
{
|
|
358
|
+
"permission_id": permission.uuid,
|
|
359
|
+
"created_at": permission.created_at,
|
|
360
|
+
"role_id": role.uuid,
|
|
361
|
+
"updated_at": permission.updated_at,
|
|
362
|
+
"role_name": role.name,
|
|
363
|
+
"name": permission.name,
|
|
364
|
+
"resource": permission.resource,
|
|
365
|
+
"action": permission.action,
|
|
366
|
+
}
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return result
|
|
370
|
+
|
|
371
|
+
def bulk_revoke_permissions(
|
|
372
|
+
self, role_uuid: str, permission_uuids: List[str]
|
|
373
|
+
) -> Any:
|
|
374
|
+
"""Revoke multiple permissions from a role"""
|
|
375
|
+
with self.db() as session:
|
|
376
|
+
try:
|
|
377
|
+
if not session.is_active:
|
|
378
|
+
session.begin()
|
|
379
|
+
|
|
380
|
+
# Get role with eager loading of permissions
|
|
381
|
+
role = (
|
|
382
|
+
session.query(Role)
|
|
383
|
+
.options(joinedload(Role.permissions))
|
|
384
|
+
.filter(Role.uuid == role_uuid)
|
|
385
|
+
.first()
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if not role:
|
|
389
|
+
raise NotFoundError(detail="Requested role does not exist")
|
|
390
|
+
|
|
391
|
+
# Filter permissions to revoke from the eagerly loaded permissions
|
|
392
|
+
permissions_to_revoke = [
|
|
393
|
+
p for p in role.permissions
|
|
394
|
+
if p.uuid in permission_uuids
|
|
395
|
+
]
|
|
396
|
+
|
|
397
|
+
if not permissions_to_revoke:
|
|
398
|
+
return role
|
|
399
|
+
|
|
400
|
+
# Get UUIDs of permissions to revoke
|
|
401
|
+
permission_uuids_to_revoke = [p.uuid for p in permissions_to_revoke]
|
|
402
|
+
|
|
403
|
+
# Delete role permissions
|
|
404
|
+
session.query(RolePermission).filter(
|
|
405
|
+
and_(
|
|
406
|
+
RolePermission.role_uuid == role_uuid,
|
|
407
|
+
RolePermission.permission_uuid.in_(permission_uuids_to_revoke),
|
|
408
|
+
)
|
|
409
|
+
).delete(synchronize_session=False)
|
|
410
|
+
|
|
411
|
+
# Remove Casbin policies
|
|
412
|
+
policies_to_remove = [
|
|
413
|
+
(role.name, permission.resource, permission.action, permission.module)
|
|
414
|
+
for permission in permissions_to_revoke
|
|
415
|
+
]
|
|
416
|
+
self.enforcer.remove_policies(policies_to_remove)
|
|
417
|
+
self.enforcer.save_policy()
|
|
418
|
+
|
|
419
|
+
session.commit()
|
|
420
|
+
|
|
421
|
+
# Refresh the role to get the updated permissions
|
|
422
|
+
session.refresh(role)
|
|
423
|
+
return role
|
|
424
|
+
|
|
425
|
+
except Exception as e:
|
|
426
|
+
raise e
|
|
427
|
+
|
|
428
|
+
def bulk_attach_permissions(
|
|
429
|
+
self, role_uuid: str, permission_uuids: List[str]
|
|
430
|
+
) -> Any:
|
|
431
|
+
"""Attach multiple permissions to a role"""
|
|
432
|
+
with self.db() as session:
|
|
433
|
+
try:
|
|
434
|
+
if not session.is_active:
|
|
435
|
+
session.begin()
|
|
436
|
+
|
|
437
|
+
# Get role with eager loading of permissions
|
|
438
|
+
role = (
|
|
439
|
+
session.query(Role)
|
|
440
|
+
.options(joinedload(Role.permissions))
|
|
441
|
+
.filter(Role.uuid == role_uuid)
|
|
442
|
+
.first()
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
if not role:
|
|
446
|
+
raise NotFoundError(detail="Requested role does not exist")
|
|
447
|
+
|
|
448
|
+
# Get existing permission UUIDs from the eagerly loaded permissions
|
|
449
|
+
existing_permission_uuids = {p.uuid for p in role.permissions}
|
|
450
|
+
|
|
451
|
+
# Calculate new permission UUIDs to attach
|
|
452
|
+
new_permission_uuids = set(permission_uuids) - existing_permission_uuids
|
|
453
|
+
|
|
454
|
+
if not new_permission_uuids:
|
|
455
|
+
return role
|
|
456
|
+
|
|
457
|
+
# Fetch new permissions in a single query
|
|
458
|
+
new_permissions = (
|
|
459
|
+
session.query(Permission)
|
|
460
|
+
.filter(Permission.uuid.in_(new_permission_uuids))
|
|
461
|
+
.all()
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# Verify all permissions were found
|
|
465
|
+
if len(new_permissions) != len(new_permission_uuids):
|
|
466
|
+
found_permission_uuids = {p.uuid for p in new_permissions}
|
|
467
|
+
missing_permission_uuids = new_permission_uuids - found_permission_uuids
|
|
468
|
+
raise NotFoundError(
|
|
469
|
+
detail=f"Permissions with UUIDs '{', '.join(missing_permission_uuids)}' not found"
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
# Bulk insert role permissions
|
|
473
|
+
role_permissions = [
|
|
474
|
+
{"role_uuid": role_uuid, "permission_uuid": p.uuid}
|
|
475
|
+
for p in new_permissions
|
|
476
|
+
]
|
|
477
|
+
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
478
|
+
|
|
479
|
+
# Add Casbin policies
|
|
480
|
+
policies_to_add = [
|
|
481
|
+
(role.name, permission.resource, permission.action, permission.module)
|
|
482
|
+
for permission in new_permissions
|
|
483
|
+
]
|
|
484
|
+
self.enforcer.add_policies(policies_to_add)
|
|
485
|
+
self.enforcer.save_policy()
|
|
486
|
+
|
|
487
|
+
session.commit()
|
|
488
|
+
|
|
489
|
+
# Refresh the role to get the updated permissions
|
|
490
|
+
session.refresh(role)
|
|
491
|
+
return role
|
|
492
|
+
|
|
493
|
+
except Exception as e:
|
|
494
|
+
raise e
|
|
495
|
+
|
|
496
|
+
def get_user_roles(self, user_uuid: str,session: Optional[Session] = None) -> List[Any]:
|
|
497
|
+
"""Get user roles"""
|
|
498
|
+
def query_roles(session: Session) -> List[Any]:
|
|
499
|
+
return (
|
|
500
|
+
session.query(Role)
|
|
501
|
+
.join(
|
|
502
|
+
UserRole,
|
|
503
|
+
and_(
|
|
504
|
+
UserRole.role_uuid == Role.uuid,
|
|
505
|
+
UserRole.user_uuid == user_uuid
|
|
506
|
+
)
|
|
507
|
+
)
|
|
508
|
+
.options(joinedload(Role.permissions))
|
|
509
|
+
.all()
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
if session:
|
|
513
|
+
return query_roles(session)
|
|
514
|
+
else:
|
|
515
|
+
with self.db() as new_session:
|
|
516
|
+
return query_roles(new_session)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def bulk_assign_roles_to_user(
|
|
520
|
+
self, user_uuid: str, role_uuids: List[str]
|
|
521
|
+
) -> List[Any]:
|
|
522
|
+
"""Assign multiple roles to a user"""
|
|
523
|
+
with self.db() as session:
|
|
524
|
+
try:
|
|
525
|
+
if not session.is_active:
|
|
526
|
+
session.begin()
|
|
527
|
+
|
|
528
|
+
current_roles = (
|
|
529
|
+
session.query(UserRole)
|
|
530
|
+
.options(joinedload(UserRole.role))
|
|
531
|
+
.filter(UserRole.user_uuid == user_uuid)
|
|
532
|
+
.all()
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
current_role_uuids = {role.role.uuid for role in current_roles}
|
|
536
|
+
|
|
537
|
+
new_role_uuids = set(role_uuids) - current_role_uuids
|
|
538
|
+
|
|
539
|
+
roles_to_remove = current_role_uuids - set(role_uuids)
|
|
540
|
+
|
|
541
|
+
if roles_to_remove:
|
|
542
|
+
session.query(UserRole).filter(
|
|
543
|
+
and_(
|
|
544
|
+
UserRole.user_uuid == user_uuid,
|
|
545
|
+
UserRole.role_uuid.in_(roles_to_remove),
|
|
546
|
+
)
|
|
547
|
+
).delete(synchronize_session=False)
|
|
548
|
+
|
|
549
|
+
if new_role_uuids:
|
|
550
|
+
new_roles = (
|
|
551
|
+
session.query(Role).filter(Role.uuid.in_(new_role_uuids)).all()
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
if len(new_roles) != len(new_role_uuids):
|
|
555
|
+
raise NotFoundError(detail="One or more roles not found")
|
|
556
|
+
|
|
557
|
+
user_roles = [
|
|
558
|
+
UserRole(user_uuid=user_uuid, role_uuid=role.uuid)
|
|
559
|
+
for role in new_roles
|
|
560
|
+
]
|
|
561
|
+
session.bulk_save_objects(user_roles)
|
|
562
|
+
|
|
563
|
+
session.commit()
|
|
564
|
+
|
|
565
|
+
return self.get_user_roles(user_uuid,session)
|
|
566
|
+
|
|
567
|
+
except Exception as e:
|
|
568
|
+
raise e
|
|
569
|
+
|
|
570
|
+
# Bulk Revoke Roles From User
|
|
571
|
+
def bulk_revoke_roles_from_user(
|
|
572
|
+
self, user_uuid: str, role_uuids: List[str]
|
|
573
|
+
) -> List[Any]:
|
|
574
|
+
"""Revoke multiple roles from a user"""
|
|
575
|
+
with self.db() as session:
|
|
576
|
+
try:
|
|
577
|
+
if not session.is_active:
|
|
578
|
+
session.begin()
|
|
579
|
+
|
|
580
|
+
current_roles = (
|
|
581
|
+
session.query(UserRole)
|
|
582
|
+
.options(joinedload(UserRole.role))
|
|
583
|
+
.filter(UserRole.user_uuid == user_uuid)
|
|
584
|
+
.filter(UserRole.role_uuid.in_(role_uuids))
|
|
585
|
+
.all()
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
if not current_roles:
|
|
589
|
+
return self.get_user_roles(user_uuid)
|
|
590
|
+
|
|
591
|
+
role_uuids_to_revoke = {role.role.uuid for role in current_roles}
|
|
592
|
+
|
|
593
|
+
session.query(UserRole).filter(
|
|
594
|
+
and_(
|
|
595
|
+
UserRole.user_uuid == user_uuid,
|
|
596
|
+
UserRole.role_uuid.in_(role_uuids_to_revoke),
|
|
597
|
+
)
|
|
598
|
+
).delete(synchronize_session=False)
|
|
599
|
+
|
|
600
|
+
session.commit()
|
|
601
|
+
|
|
602
|
+
return self.get_user_roles(user_uuid,session)
|
|
603
|
+
|
|
604
|
+
except Exception as e:
|
|
605
|
+
raise e
|
|
606
|
+
|
|
607
|
+
def bulk_attach_roles_to_user(
|
|
608
|
+
self, user_uuid: str, role_uuids: List[str]
|
|
609
|
+
) -> List[Any]:
|
|
610
|
+
"""Attach multiple roles to a user"""
|
|
611
|
+
with self.db() as session:
|
|
612
|
+
try:
|
|
613
|
+
if not session.is_active:
|
|
614
|
+
session.begin()
|
|
615
|
+
|
|
616
|
+
current_roles = (
|
|
617
|
+
session.query(UserRole)
|
|
618
|
+
.options(joinedload(UserRole.role))
|
|
619
|
+
.filter(UserRole.user_uuid == user_uuid)
|
|
620
|
+
.all()
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
current_role_uuids = {role.role.uuid for role in current_roles}
|
|
624
|
+
|
|
625
|
+
new_role_uuids = set(role_uuids) - current_role_uuids
|
|
626
|
+
|
|
627
|
+
if not new_role_uuids:
|
|
628
|
+
return self.get_user_roles(user_uuid)
|
|
629
|
+
|
|
630
|
+
new_roles = (
|
|
631
|
+
session.query(Role).filter(Role.uuid.in_(new_role_uuids)).all()
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
if len(new_roles) != len(new_role_uuids):
|
|
635
|
+
raise NotFoundError(detail="There are some roles that does not exist.")
|
|
636
|
+
|
|
637
|
+
user_roles = [
|
|
638
|
+
UserRole(user_uuid=user_uuid, role_uuid=role.uuid) for role in new_roles
|
|
639
|
+
]
|
|
640
|
+
session.bulk_save_objects(user_roles)
|
|
641
|
+
|
|
642
|
+
session.commit()
|
|
643
|
+
|
|
644
|
+
return self.get_user_roles(user_uuid,session)
|
|
645
|
+
|
|
646
|
+
except Exception as e:
|
|
647
|
+
raise e
|
|
648
|
+
|
|
649
|
+
def check_permission(self, user_uuid: str, resource: str, action: str, module: str) -> bool:
|
|
650
|
+
with self.db() as session:
|
|
651
|
+
roles = (
|
|
652
|
+
session.query(Role)
|
|
653
|
+
.join(
|
|
654
|
+
UserRole,
|
|
655
|
+
and_(
|
|
656
|
+
UserRole.role_uuid == Role.uuid,
|
|
657
|
+
UserRole.user_uuid == user_uuid,
|
|
658
|
+
),
|
|
659
|
+
)
|
|
660
|
+
.all()
|
|
661
|
+
)
|
|
662
|
+
for role in roles:
|
|
663
|
+
# Try with module first
|
|
664
|
+
if self.enforcer.enforce(role.name, resource, action, module):
|
|
665
|
+
return True
|
|
666
|
+
return False
|
|
667
|
+
|
|
668
|
+
def check_permission_by_role(
|
|
669
|
+
self, role_name: str, resource: str, action: str, module: str
|
|
670
|
+
) -> bool:
|
|
671
|
+
# Try with module first
|
|
672
|
+
if self.enforcer.enforce(role_name, resource, action, module):
|
|
673
|
+
return True
|
|
674
|
+
return False
|
|
675
|
+
|
|
676
|
+
def get_role(self, role_uuid: str,session: Optional[Session] = None) -> Any:
|
|
677
|
+
"""Get role by uuid"""
|
|
678
|
+
def query_role(session: Session) -> Any:
|
|
679
|
+
role = session.query(Role).filter(Role.uuid == role_uuid).first()
|
|
680
|
+
if not role:
|
|
681
|
+
raise NotFoundError(detail="Requested role does not exist.")
|
|
682
|
+
return role
|
|
683
|
+
|
|
684
|
+
if session:
|
|
685
|
+
return query_role(session)
|
|
686
|
+
else:
|
|
687
|
+
with self.db() as session:
|
|
688
|
+
return query_role(session)
|
|
File without changes
|