abs-auth-rbac-core 0.3.2__py3-none-any.whl → 0.3.18__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.
- abs_auth_rbac_core/auth/__init__.py +13 -1
- abs_auth_rbac_core/auth/auth_functions.py +4 -1
- abs_auth_rbac_core/auth/middleware.py +166 -1
- abs_auth_rbac_core/models/user.py +1 -0
- abs_auth_rbac_core/rbac/service.py +248 -105
- abs_auth_rbac_core/repository/__init__.py +4 -0
- abs_auth_rbac_core/repository/permission_repository.py +12 -0
- abs_auth_rbac_core/repository/role_repository.py +18 -0
- abs_auth_rbac_core/service/__init__.py +4 -0
- abs_auth_rbac_core/service/permission_service.py +15 -0
- abs_auth_rbac_core/service/role_service.py +18 -0
- abs_auth_rbac_core/util/permission_constants.py +29 -1
- {abs_auth_rbac_core-0.3.2.dist-info → abs_auth_rbac_core-0.3.18.dist-info}/METADATA +6 -4
- {abs_auth_rbac_core-0.3.2.dist-info → abs_auth_rbac_core-0.3.18.dist-info}/RECORD +15 -9
- {abs_auth_rbac_core-0.3.2.dist-info → abs_auth_rbac_core-0.3.18.dist-info}/WHEEL +1 -1
|
@@ -1,3 +1,15 @@
|
|
|
1
1
|
from .jwt_functions import JWTFunctions
|
|
2
|
+
from .middleware import (
|
|
3
|
+
CustomHTTPBearer,
|
|
4
|
+
auth_middleware,
|
|
5
|
+
contacts_auth_middleware,
|
|
6
|
+
unified_auth_middleware
|
|
7
|
+
)
|
|
2
8
|
|
|
3
|
-
__all__ = [
|
|
9
|
+
__all__ = [
|
|
10
|
+
"JWTFunctions",
|
|
11
|
+
"CustomHTTPBearer",
|
|
12
|
+
"auth_middleware",
|
|
13
|
+
"contacts_auth_middleware",
|
|
14
|
+
"unified_auth_middleware"
|
|
15
|
+
]
|
|
@@ -20,7 +20,10 @@ def get_user_by_attribute(db_session: Callable[...,Any],attribute: str, value: s
|
|
|
20
20
|
if not hasattr(Users, attribute):
|
|
21
21
|
raise ValidationError(detail=f"Attribute {attribute} does not exist on the User model")
|
|
22
22
|
|
|
23
|
-
user = session.query(Users).filter(
|
|
23
|
+
user = session.query(Users).filter(
|
|
24
|
+
getattr(Users, attribute) == value,
|
|
25
|
+
Users.deleted_at.is_(None) # Filter out soft-deleted users
|
|
26
|
+
).first()
|
|
24
27
|
|
|
25
28
|
if not user:
|
|
26
29
|
raise NotFoundError(detail="User not found")
|
|
@@ -6,7 +6,8 @@ from typing import Callable, Any, Optional
|
|
|
6
6
|
|
|
7
7
|
from .jwt_functions import JWTFunctions
|
|
8
8
|
from .auth_functions import get_user_by_attribute
|
|
9
|
-
from abs_exception_core.exceptions import UnauthorizedError, AuthError
|
|
9
|
+
from abs_exception_core.exceptions import UnauthorizedError, AuthError, NotFoundError
|
|
10
|
+
from abs_nosql_repository_core.repository import BaseRepository
|
|
10
11
|
from fastapi.security.utils import get_authorization_scheme_param
|
|
11
12
|
|
|
12
13
|
class CustomHTTPBearer(HTTPBearer):
|
|
@@ -77,4 +78,168 @@ def auth_middleware(
|
|
|
77
78
|
logger.error(f"Authentication error: {str(e)}", exc_info=True)
|
|
78
79
|
raise UnauthorizedError(detail="Authentication failed")
|
|
79
80
|
|
|
81
|
+
return get_auth
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def contacts_auth_middleware(
|
|
85
|
+
entity_records_service,
|
|
86
|
+
jwt_secret_key: str,
|
|
87
|
+
jwt_algorithm: str
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
Contact authentication middleware using EntityRecordsService.
|
|
91
|
+
|
|
92
|
+
This middleware validates contact JWT tokens and attaches contact data to request.state.
|
|
93
|
+
|
|
94
|
+
JWT Payload Structure:
|
|
95
|
+
{
|
|
96
|
+
"sub": "contact@example.com",
|
|
97
|
+
"cid": "contact_record_id", # KEY IDENTIFIER for contacts
|
|
98
|
+
"uuid": "unique-uuid",
|
|
99
|
+
"exp": 1234567890,
|
|
100
|
+
"iat": 1234567890
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
entity_records_service: EntityRecordsService for entity operations
|
|
105
|
+
jwt_secret_key: JWT secret key
|
|
106
|
+
jwt_algorithm: JWT algorithm (e.g., HS256)
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
FastAPI dependency function that validates contact authentication
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
async def get_auth(
|
|
113
|
+
request: Request,
|
|
114
|
+
token: HTTPAuthorizationCredentials = Depends(security)
|
|
115
|
+
):
|
|
116
|
+
jwt_functions = JWTFunctions(
|
|
117
|
+
secret_key=jwt_secret_key,
|
|
118
|
+
algorithm=jwt_algorithm
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
if not token or not token.credentials:
|
|
123
|
+
raise UnauthorizedError(detail="Invalid authentication credentials")
|
|
124
|
+
|
|
125
|
+
# Decode JWT and extract payload
|
|
126
|
+
payload = jwt_functions.get_data(token=token.credentials)
|
|
127
|
+
|
|
128
|
+
# Extract cid (required for contact authentication)
|
|
129
|
+
cid = payload.get("cid")
|
|
130
|
+
if not cid:
|
|
131
|
+
raise UnauthorizedError(detail="Invalid contact token")
|
|
132
|
+
|
|
133
|
+
# Extract app_id from path parameters
|
|
134
|
+
app_id = request.path_params.get("app_id")
|
|
135
|
+
if not app_id:
|
|
136
|
+
raise UnauthorizedError(detail="App ID is required")
|
|
137
|
+
|
|
138
|
+
logger.info(f"Contact auth: app_id={app_id}, cid={cid}")
|
|
139
|
+
|
|
140
|
+
# Get app configuration (apps collection is not an entity collection)
|
|
141
|
+
# We still need BaseRepository for this until apps get moved to a service
|
|
142
|
+
base_repo = BaseRepository(db=entity_records_service.repository.db)
|
|
143
|
+
app = await base_repo.get_by_attr("id", app_id, collection_name="apps")
|
|
144
|
+
if not app:
|
|
145
|
+
logger.error(f"App not found: {app_id}")
|
|
146
|
+
raise UnauthorizedError(detail="App not found")
|
|
147
|
+
|
|
148
|
+
# Extract contact entity ID from app metadata
|
|
149
|
+
metadata = app.get("metadata", {})
|
|
150
|
+
if not metadata:
|
|
151
|
+
raise UnauthorizedError(detail="App doesn't have support for contacts")
|
|
152
|
+
|
|
153
|
+
features_entities = metadata.get("features_entities", {})
|
|
154
|
+
if "CONTACT" not in features_entities:
|
|
155
|
+
raise UnauthorizedError(detail="App doesn't have support for contacts")
|
|
156
|
+
|
|
157
|
+
contact_config = features_entities.get("CONTACT", {})
|
|
158
|
+
contact_entity_id = contact_config.get("Contacts")
|
|
159
|
+
|
|
160
|
+
if not contact_entity_id:
|
|
161
|
+
raise UnauthorizedError(detail="Contact entity not configured")
|
|
162
|
+
|
|
163
|
+
logger.info(f"Contact entity ID: {contact_entity_id}")
|
|
164
|
+
|
|
165
|
+
# Get contact record using EntityRecordsService
|
|
166
|
+
try:
|
|
167
|
+
contact = await entity_records_service.get_record_by_id(
|
|
168
|
+
entity_id=contact_entity_id,
|
|
169
|
+
record_id=cid,
|
|
170
|
+
convert_ids=False # Keep ObjectIds for internal use
|
|
171
|
+
)
|
|
172
|
+
except NotFoundError:
|
|
173
|
+
logger.error(f"Authentication failed: contact with id {cid} not found")
|
|
174
|
+
raise UnauthorizedError(detail="Authentication failed")
|
|
175
|
+
|
|
176
|
+
logger.info(f"✅ Contact authenticated: {cid}")
|
|
177
|
+
|
|
178
|
+
# Attach contact to request state
|
|
179
|
+
request.state.contact = contact
|
|
180
|
+
request.state.contact_entity_id = contact_entity_id
|
|
181
|
+
|
|
182
|
+
return contact
|
|
183
|
+
|
|
184
|
+
except UnauthorizedError:
|
|
185
|
+
raise
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.error(f"Authentication error: {str(e)}", exc_info=True)
|
|
188
|
+
raise UnauthorizedError(detail="Authentication failed")
|
|
189
|
+
|
|
190
|
+
return get_auth
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def unified_auth_middleware(
|
|
194
|
+
jwt_secret_key: str,
|
|
195
|
+
jwt_algorithm: str,
|
|
196
|
+
get_user_auth_middleware: Callable,
|
|
197
|
+
get_contact_auth_middleware: Callable,
|
|
198
|
+
):
|
|
199
|
+
"""
|
|
200
|
+
Unified authentication middleware that intelligently routes to the correct
|
|
201
|
+
authentication flow based on JWT payload.
|
|
202
|
+
|
|
203
|
+
Detection Logic:
|
|
204
|
+
- If JWT contains "cid" field → Contact authentication
|
|
205
|
+
- Otherwise → User authentication
|
|
206
|
+
|
|
207
|
+
Benefits:
|
|
208
|
+
- Single entry point for all authentication
|
|
209
|
+
- JWT decoded only once for efficiency
|
|
210
|
+
- Transparent routing based on token type
|
|
211
|
+
- No code changes needed in route handlers
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
jwt_secret_key: JWT secret key
|
|
215
|
+
jwt_algorithm: JWT algorithm (e.g., HS256)
|
|
216
|
+
get_user_auth_middleware: User auth middleware callable
|
|
217
|
+
get_contact_auth_middleware: Contact auth middleware callable
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
FastAPI dependency that handles unified authentication
|
|
221
|
+
"""
|
|
222
|
+
async def get_auth(
|
|
223
|
+
request: Request,
|
|
224
|
+
token: HTTPAuthorizationCredentials = Depends(security)
|
|
225
|
+
):
|
|
226
|
+
if not token or not token.credentials:
|
|
227
|
+
raise UnauthorizedError(detail="Invalid authentication credentials")
|
|
228
|
+
|
|
229
|
+
# Decode JWT once to inspect payload
|
|
230
|
+
jwt_functions = JWTFunctions(
|
|
231
|
+
secret_key=jwt_secret_key,
|
|
232
|
+
algorithm=jwt_algorithm
|
|
233
|
+
)
|
|
234
|
+
payload = jwt_functions.get_data(token=token.credentials)
|
|
235
|
+
|
|
236
|
+
# Route based on payload content
|
|
237
|
+
cid = payload.get("cid")
|
|
238
|
+
if cid:
|
|
239
|
+
# Contact flow: JWT contains contact ID
|
|
240
|
+
return await get_contact_auth_middleware(request=request, token=token)
|
|
241
|
+
|
|
242
|
+
# User flow: Standard user authentication
|
|
243
|
+
return await get_user_auth_middleware(request=request, token=token)
|
|
244
|
+
|
|
80
245
|
return get_auth
|
|
@@ -12,6 +12,7 @@ class Users(BaseModel):
|
|
|
12
12
|
name = Column(String(100), nullable=False)
|
|
13
13
|
is_active = Column(Boolean, default=True)
|
|
14
14
|
last_login_at = Column(DateTime, nullable=True)
|
|
15
|
+
deleted_at = Column(DateTime, nullable=True) # Soft delete timestamp
|
|
15
16
|
|
|
16
17
|
# Relationships
|
|
17
18
|
roles = relationship(
|
|
@@ -4,7 +4,7 @@ from pydantic import BaseModel
|
|
|
4
4
|
import casbin
|
|
5
5
|
from casbin_sqlalchemy_adapter import Adapter
|
|
6
6
|
from casbin_redis_watcher import RedisWatcher, WatcherOptions,new_watcher
|
|
7
|
-
from sqlalchemy import and_, select
|
|
7
|
+
from sqlalchemy import and_, or_, select
|
|
8
8
|
from sqlalchemy.orm import Session, joinedload
|
|
9
9
|
from ..schema import CreatePermissionSchema
|
|
10
10
|
from ..models import (
|
|
@@ -47,6 +47,43 @@ class RBACService:
|
|
|
47
47
|
self.watcher = None
|
|
48
48
|
|
|
49
49
|
|
|
50
|
+
def _save_policy_if_watcher_active(self):
|
|
51
|
+
"""
|
|
52
|
+
Helper method to save policy only if Redis watcher is active.
|
|
53
|
+
This ensures distributed systems stay in sync while avoiding
|
|
54
|
+
unnecessary load_policy calls on the current instance.
|
|
55
|
+
"""
|
|
56
|
+
if self.is_watcher_active():
|
|
57
|
+
self.enforcer.save_policy()
|
|
58
|
+
|
|
59
|
+
def _bulk_operation_context(self):
|
|
60
|
+
"""
|
|
61
|
+
Context manager for bulk Casbin operations.
|
|
62
|
+
Temporarily disables auto_save, executes operations, then saves once at the end.
|
|
63
|
+
This significantly reduces overhead for bulk policy changes.
|
|
64
|
+
|
|
65
|
+
Usage:
|
|
66
|
+
with self._bulk_operation_context():
|
|
67
|
+
self.enforcer.add_policies(policies)
|
|
68
|
+
"""
|
|
69
|
+
class BulkOperationContext:
|
|
70
|
+
def __init__(self, service):
|
|
71
|
+
self.service = service
|
|
72
|
+
self.original_auto_save = None
|
|
73
|
+
|
|
74
|
+
def __enter__(self):
|
|
75
|
+
self.original_auto_save = self.service.enforcer.auto_save
|
|
76
|
+
self.service.enforcer.enable_auto_save(False)
|
|
77
|
+
return self
|
|
78
|
+
|
|
79
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
80
|
+
if exc_type is None: # Only save if no exception occurred
|
|
81
|
+
self.service._save_policy_if_watcher_active()
|
|
82
|
+
self.service.enforcer.enable_auto_save(self.original_auto_save)
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
return BulkOperationContext(self)
|
|
86
|
+
|
|
50
87
|
def _initialize_casbin(self,redis_config:Optional[RedisWatcherSchema]=None):
|
|
51
88
|
"""
|
|
52
89
|
Initiates the casbin policy using the default rules
|
|
@@ -65,8 +102,7 @@ class RBACService:
|
|
|
65
102
|
self.enforcer = casbin.Enforcer(
|
|
66
103
|
policy_path, adapter
|
|
67
104
|
)
|
|
68
|
-
|
|
69
|
-
self.enforcer.load_policy()
|
|
105
|
+
self.enforcer.enable_auto_save(True)
|
|
70
106
|
|
|
71
107
|
if redis_config:
|
|
72
108
|
try:
|
|
@@ -109,8 +145,6 @@ class RBACService:
|
|
|
109
145
|
|
|
110
146
|
self.enforcer.set_watcher(watcher)
|
|
111
147
|
self.watcher = watcher
|
|
112
|
-
self.enforcer.save_policy()
|
|
113
|
-
|
|
114
148
|
except Exception as e:
|
|
115
149
|
logger.error(f"Failed to initialize Redis watcher: {e}")
|
|
116
150
|
self.watcher = None
|
|
@@ -119,31 +153,31 @@ class RBACService:
|
|
|
119
153
|
|
|
120
154
|
def add_policy(self,role:str,resource:str,action:str,module:str):
|
|
121
155
|
"""
|
|
122
|
-
Add a policy to the casbin enforcer
|
|
156
|
+
Add a policy to the casbin enforcer (optimized for distributed systems)
|
|
123
157
|
"""
|
|
124
|
-
self.
|
|
125
|
-
|
|
158
|
+
with self._bulk_operation_context():
|
|
159
|
+
self.enforcer.add_policy(role,resource,action,module)
|
|
126
160
|
|
|
127
161
|
def remove_policy(self,role:str,resource:str,action:str,module:str):
|
|
128
162
|
"""
|
|
129
|
-
Remove a policy from the casbin enforcer
|
|
163
|
+
Remove a policy from the casbin enforcer (optimized for distributed systems)
|
|
130
164
|
"""
|
|
131
|
-
self.
|
|
132
|
-
|
|
165
|
+
with self._bulk_operation_context():
|
|
166
|
+
self.enforcer.remove_policy(role,resource,action,module)
|
|
133
167
|
|
|
134
168
|
def add_policies(self,policies:List[Tuple[str,str,str,str]]):
|
|
135
169
|
"""
|
|
136
|
-
Add a list of policies to the casbin enforcer
|
|
170
|
+
Add a list of policies to the casbin enforcer (optimized for distributed systems)
|
|
137
171
|
"""
|
|
138
|
-
self.
|
|
139
|
-
|
|
172
|
+
with self._bulk_operation_context():
|
|
173
|
+
self.enforcer.add_policies(policies)
|
|
140
174
|
|
|
141
175
|
def remove_policies(self,policies:List[List[str]]):
|
|
142
176
|
"""
|
|
143
|
-
Remove a list of policies from the casbin enforcer
|
|
177
|
+
Remove a list of policies from the casbin enforcer (optimized for distributed systems)
|
|
144
178
|
"""
|
|
145
|
-
self.
|
|
146
|
-
|
|
179
|
+
with self._bulk_operation_context():
|
|
180
|
+
self.enforcer.remove_policies(policies)
|
|
147
181
|
|
|
148
182
|
def enforce_policy(self,role:str,resource:str,action:str,module:str):
|
|
149
183
|
"""
|
|
@@ -153,13 +187,13 @@ class RBACService:
|
|
|
153
187
|
|
|
154
188
|
def remove_filter_policy(self,index:int,value:str):
|
|
155
189
|
"""
|
|
156
|
-
Remove a policy by filtering the policy
|
|
190
|
+
Remove a policy by filtering the policy (optimized for distributed systems)
|
|
157
191
|
Args:
|
|
158
192
|
index: The index of the policy to remove
|
|
159
193
|
value: The value of the policy to remove
|
|
160
194
|
"""
|
|
161
|
-
self.
|
|
162
|
-
|
|
195
|
+
with self._bulk_operation_context():
|
|
196
|
+
self.enforcer.remove_filtered_policy(index,value)
|
|
163
197
|
|
|
164
198
|
async def bulk_create_permissions(self,permissions:List[CreatePermissionSchema]):
|
|
165
199
|
"""
|
|
@@ -181,34 +215,64 @@ class RBACService:
|
|
|
181
215
|
except Exception as e:
|
|
182
216
|
raise e
|
|
183
217
|
|
|
184
|
-
|
|
218
|
+
def build_filter(self,cond: dict):
|
|
219
|
+
if "and" in cond:
|
|
220
|
+
return and_(*[self.build_filter(c) for c in cond["and"]])
|
|
221
|
+
elif "or" in cond:
|
|
222
|
+
return or_(*[self.build_filter(c) for c in cond["or"]])
|
|
223
|
+
else:
|
|
224
|
+
# Multiple simple field=value pairs in the same dict
|
|
225
|
+
return and_(*[
|
|
226
|
+
getattr(Permission, field) == value
|
|
227
|
+
for field, value in cond.items()
|
|
228
|
+
])
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
async def get_permissions_by_condition(self, condition: dict):
|
|
185
232
|
"""
|
|
186
|
-
Get permission(s) based on
|
|
187
|
-
|
|
233
|
+
Get permission(s) based on nested logical conditions.
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
{
|
|
237
|
+
"and": [
|
|
238
|
+
{"entity_id": "123"},
|
|
239
|
+
{"or": [
|
|
240
|
+
{"user_id": "456"},
|
|
241
|
+
{"group_id": "789"}
|
|
242
|
+
]}
|
|
243
|
+
]
|
|
244
|
+
}
|
|
188
245
|
"""
|
|
189
246
|
with self.db() as session:
|
|
190
247
|
try:
|
|
191
|
-
query = session.query(Permission)
|
|
192
|
-
if use_filter_by:
|
|
193
|
-
query = query.filter_by(**condition)
|
|
194
|
-
else:
|
|
195
|
-
filters = [getattr(Permission, k) == v for k, v in condition.items()]
|
|
196
|
-
query = query.filter(*filters)
|
|
248
|
+
query = session.query(Permission).filter(self.build_filter(condition))
|
|
197
249
|
return query.all()
|
|
198
250
|
except Exception as e:
|
|
199
251
|
raise e
|
|
200
|
-
|
|
252
|
+
|
|
201
253
|
async def delete_permission_by_uuids(self,permission_uuids:List[str]):
|
|
202
254
|
"""
|
|
203
|
-
Delete
|
|
255
|
+
Delete permissions by uuids (optimized for distributed systems)
|
|
256
|
+
Handles cascade deletion in proper order:
|
|
257
|
+
1. Delete UserPermission associations
|
|
258
|
+
2. Delete RolePermission associations
|
|
259
|
+
3. Delete Permissions
|
|
204
260
|
"""
|
|
205
261
|
with self.db() as session:
|
|
206
262
|
try:
|
|
263
|
+
# Step 1: Delete user_permissions associations
|
|
207
264
|
user_permissions = session.query(UserPermission).filter(UserPermission.permission_uuid.in_(permission_uuids)).delete(synchronize_session=False)
|
|
265
|
+
|
|
266
|
+
# Step 2: Delete role_permissions associations (CRITICAL for FK constraint)
|
|
267
|
+
role_permissions = session.query(RolePermission).filter(RolePermission.permission_uuid.in_(permission_uuids)).delete(synchronize_session=False)
|
|
268
|
+
|
|
269
|
+
# Step 3: Delete permissions
|
|
208
270
|
permissions = session.query(Permission).filter(Permission.uuid.in_(permission_uuids))
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
271
|
+
|
|
272
|
+
with self._bulk_operation_context():
|
|
273
|
+
for permission in permissions:
|
|
274
|
+
self.enforcer.remove_filtered_policy(1,permission.resource)
|
|
275
|
+
|
|
212
276
|
permissions.delete(synchronize_session=False)
|
|
213
277
|
session.commit()
|
|
214
278
|
return True
|
|
@@ -234,29 +298,38 @@ class RBACService:
|
|
|
234
298
|
raise e
|
|
235
299
|
|
|
236
300
|
def attach_permissions_to_user(self, user_uuid: str, permission_uuids: List[str]):
|
|
301
|
+
"""
|
|
302
|
+
Attach permissions to user (optimized for distributed systems)
|
|
303
|
+
"""
|
|
237
304
|
with self.db() as session:
|
|
238
305
|
try:
|
|
239
|
-
|
|
240
|
-
|
|
306
|
+
# Use bulk_insert_mappings for better performance
|
|
307
|
+
user_permissions_data = [
|
|
308
|
+
{"user_uuid": user_uuid, "permission_uuid": permission_uuid}
|
|
241
309
|
for permission_uuid in permission_uuids
|
|
242
310
|
]
|
|
243
|
-
session.
|
|
311
|
+
session.bulk_insert_mappings(UserPermission, user_permissions_data)
|
|
244
312
|
session.commit()
|
|
245
313
|
|
|
314
|
+
# Fetch permissions and build policies
|
|
246
315
|
permissions = session.query(Permission).filter(Permission.uuid.in_(permission_uuids)).all()
|
|
247
316
|
policies = [
|
|
248
317
|
[f"user:{user_uuid}", permission.resource, permission.action, permission.module]
|
|
249
318
|
for permission in permissions
|
|
250
319
|
]
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
self.enforcer.
|
|
320
|
+
|
|
321
|
+
# Use context manager for optimized bulk operation
|
|
322
|
+
with self._bulk_operation_context():
|
|
323
|
+
self.enforcer.add_policies(policies)
|
|
324
|
+
|
|
255
325
|
return self.get_user_only_permissions(user_uuid)
|
|
256
326
|
except Exception as e:
|
|
257
327
|
raise e
|
|
258
328
|
|
|
259
329
|
def revoke_user_permissions(self, user_uuid: str, permission_uuids: List[str]):
|
|
330
|
+
"""
|
|
331
|
+
Revoke permissions from user (optimized for distributed systems)
|
|
332
|
+
"""
|
|
260
333
|
with self.db() as session:
|
|
261
334
|
try:
|
|
262
335
|
user_permissions = session.query(UserPermission).filter(
|
|
@@ -269,16 +342,91 @@ class RBACService:
|
|
|
269
342
|
[f"user:{user_uuid}", permission.resource, permission.action, permission.module]
|
|
270
343
|
for permission in permissions
|
|
271
344
|
]
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
self.enforcer.load_policy()
|
|
345
|
+
with self._bulk_operation_context():
|
|
346
|
+
self.enforcer.remove_policies(policies)
|
|
347
|
+
|
|
276
348
|
user_permissions.delete(synchronize_session=False)
|
|
277
349
|
session.commit()
|
|
278
350
|
return self.get_user_only_permissions(user_uuid)
|
|
279
351
|
except Exception as e:
|
|
280
352
|
raise e
|
|
281
|
-
|
|
353
|
+
|
|
354
|
+
def revoke_all_user_access(self, user_uuid: str) -> dict:
|
|
355
|
+
"""
|
|
356
|
+
Revoke all roles and permissions from a user in a single operation.
|
|
357
|
+
This is typically used when soft-deleting a user.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
user_uuid: The UUID of the user to revoke all access from
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
dict: Summary of revoked roles and permissions
|
|
364
|
+
"""
|
|
365
|
+
with self.db() as session:
|
|
366
|
+
try:
|
|
367
|
+
if not session.is_active:
|
|
368
|
+
session.begin()
|
|
369
|
+
|
|
370
|
+
# Get all user roles
|
|
371
|
+
user_roles = (
|
|
372
|
+
session.query(UserRole)
|
|
373
|
+
.options(joinedload(UserRole.role))
|
|
374
|
+
.filter(UserRole.user_uuid == user_uuid)
|
|
375
|
+
.all()
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
role_uuids = [ur.role.uuid for ur in user_roles] if user_roles else []
|
|
379
|
+
|
|
380
|
+
# Get all direct user permissions
|
|
381
|
+
user_permissions = (
|
|
382
|
+
session.query(UserPermission)
|
|
383
|
+
.options(joinedload(UserPermission.permission))
|
|
384
|
+
.filter(UserPermission.user_uuid == user_uuid)
|
|
385
|
+
.all()
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
permission_uuids = [up.permission.uuid for up in user_permissions] if user_permissions else []
|
|
389
|
+
|
|
390
|
+
# Revoke all roles
|
|
391
|
+
if role_uuids:
|
|
392
|
+
session.query(UserRole).filter(
|
|
393
|
+
UserRole.user_uuid == user_uuid,
|
|
394
|
+
UserRole.role_uuid.in_(role_uuids)
|
|
395
|
+
).delete(synchronize_session=False)
|
|
396
|
+
|
|
397
|
+
# Revoke all direct permissions and remove from Casbin
|
|
398
|
+
if permission_uuids:
|
|
399
|
+
permissions = session.query(Permission).filter(
|
|
400
|
+
Permission.uuid.in_(permission_uuids)
|
|
401
|
+
).all()
|
|
402
|
+
|
|
403
|
+
# Remove Casbin policies (optimized)
|
|
404
|
+
policies = [
|
|
405
|
+
[f"user:{user_uuid}", permission.resource, permission.action, permission.module]
|
|
406
|
+
for permission in permissions
|
|
407
|
+
]
|
|
408
|
+
with self._bulk_operation_context():
|
|
409
|
+
self.enforcer.remove_policies(policies)
|
|
410
|
+
|
|
411
|
+
# Delete from database
|
|
412
|
+
session.query(UserPermission).filter(
|
|
413
|
+
UserPermission.user_uuid == user_uuid,
|
|
414
|
+
UserPermission.permission_uuid.in_(permission_uuids)
|
|
415
|
+
).delete(synchronize_session=False)
|
|
416
|
+
|
|
417
|
+
session.commit()
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
"user_uuid": user_uuid,
|
|
421
|
+
"roles_revoked": len(role_uuids),
|
|
422
|
+
"permissions_revoked": len(permission_uuids),
|
|
423
|
+
"role_uuids": role_uuids,
|
|
424
|
+
"permission_uuids": permission_uuids
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
except Exception as e:
|
|
428
|
+
raise e
|
|
429
|
+
|
|
282
430
|
def list_roles(self) -> Any:
|
|
283
431
|
"""
|
|
284
432
|
Get the list of all roles
|
|
@@ -362,13 +510,13 @@ class RBACService:
|
|
|
362
510
|
]
|
|
363
511
|
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
364
512
|
|
|
365
|
-
# Batch add Casbin policies
|
|
513
|
+
# Batch add Casbin policies (optimized)
|
|
366
514
|
policies = [
|
|
367
515
|
[role.uuid, permission.resource, permission.action, permission.module]
|
|
368
516
|
for permission in existing_permissions
|
|
369
517
|
]
|
|
370
|
-
self.
|
|
371
|
-
|
|
518
|
+
with self._bulk_operation_context():
|
|
519
|
+
self.enforcer.add_policies(policies)
|
|
372
520
|
|
|
373
521
|
# Commit transaction
|
|
374
522
|
session.commit()
|
|
@@ -392,7 +540,7 @@ class RBACService:
|
|
|
392
540
|
if not role:
|
|
393
541
|
raise NotFoundError(detail="Requested role does not exist")
|
|
394
542
|
|
|
395
|
-
return role
|
|
543
|
+
return role
|
|
396
544
|
|
|
397
545
|
def update_role_permissions(
|
|
398
546
|
self,
|
|
@@ -402,6 +550,7 @@ class RBACService:
|
|
|
402
550
|
description: Optional[str] = None,
|
|
403
551
|
) -> Any:
|
|
404
552
|
"""Update role permissions by replacing all existing permissions with new ones"""
|
|
553
|
+
|
|
405
554
|
with self.db() as session:
|
|
406
555
|
try:
|
|
407
556
|
if not session.is_active:
|
|
@@ -438,50 +587,45 @@ class RBACService:
|
|
|
438
587
|
role.description = description
|
|
439
588
|
|
|
440
589
|
if permissions is not None:
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
[role_uuid, permission.resource, permission.action, permission.module]
|
|
481
|
-
for permission in permissions_objs
|
|
482
|
-
]
|
|
483
|
-
self.enforcer.add_policies(policies)
|
|
484
|
-
self.enforcer.save_policy()
|
|
590
|
+
# Update permissions with optimized Casbin operations
|
|
591
|
+
with self._bulk_operation_context():
|
|
592
|
+
# Remove ALL existing policies for this role from Casbin
|
|
593
|
+
self.enforcer.remove_filtered_policy(0, str(role_uuid))
|
|
594
|
+
|
|
595
|
+
# Delete existing role permissions from database
|
|
596
|
+
session.query(RolePermission).filter(
|
|
597
|
+
RolePermission.role_uuid == role_uuid
|
|
598
|
+
).delete(synchronize_session=False)
|
|
599
|
+
|
|
600
|
+
# Add new permissions if provided
|
|
601
|
+
if permissions:
|
|
602
|
+
# Fetch all permissions in a single query
|
|
603
|
+
permissions_objs = (
|
|
604
|
+
session.query(Permission)
|
|
605
|
+
.filter(Permission.uuid.in_(permissions))
|
|
606
|
+
.all()
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
found_permission_ids = {p.uuid for p in permissions_objs}
|
|
610
|
+
missing_permission_ids = set(permissions) - found_permission_ids
|
|
611
|
+
if missing_permission_ids:
|
|
612
|
+
raise NotFoundError(
|
|
613
|
+
detail=f"Permissions with UUIDs '{', '.join(missing_permission_ids)}' not found"
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
# Bulk insert role permissions
|
|
617
|
+
role_permissions = [
|
|
618
|
+
{"role_uuid": role_uuid, "permission_uuid": permission.uuid}
|
|
619
|
+
for permission in permissions_objs
|
|
620
|
+
]
|
|
621
|
+
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
622
|
+
|
|
623
|
+
# Add new Casbin policies
|
|
624
|
+
policies = [
|
|
625
|
+
[role_uuid, permission.resource, permission.action, permission.module]
|
|
626
|
+
for permission in permissions_objs
|
|
627
|
+
]
|
|
628
|
+
self.enforcer.add_policies(policies)
|
|
485
629
|
|
|
486
630
|
session.commit()
|
|
487
631
|
|
|
@@ -519,12 +663,11 @@ class RBACService:
|
|
|
519
663
|
[role.uuid, permission.resource, permission.action, permission.module]
|
|
520
664
|
for permission in role.permissions
|
|
521
665
|
]
|
|
522
|
-
|
|
523
|
-
# Remove all policies at once
|
|
524
|
-
if remove_policies:
|
|
525
666
|
|
|
526
|
-
|
|
527
|
-
|
|
667
|
+
# Remove all policies at once (optimized)
|
|
668
|
+
if remove_policies:
|
|
669
|
+
with self._bulk_operation_context():
|
|
670
|
+
self.enforcer.remove_policies(remove_policies)
|
|
528
671
|
|
|
529
672
|
# Delete role (cascade will handle role_permissions and user_roles)
|
|
530
673
|
session.delete(role)
|
|
@@ -645,13 +788,13 @@ class RBACService:
|
|
|
645
788
|
)
|
|
646
789
|
).delete(synchronize_session=False)
|
|
647
790
|
|
|
648
|
-
# Remove Casbin policies
|
|
791
|
+
# Remove Casbin policies (optimized)
|
|
649
792
|
policies_to_remove = [
|
|
650
793
|
[role.uuid, permission.resource, permission.action, permission.module]
|
|
651
794
|
for permission in permissions_to_revoke
|
|
652
795
|
]
|
|
653
|
-
self.
|
|
654
|
-
|
|
796
|
+
with self._bulk_operation_context():
|
|
797
|
+
self.enforcer.remove_policies(policies_to_remove)
|
|
655
798
|
|
|
656
799
|
session.commit()
|
|
657
800
|
|
|
@@ -713,13 +856,13 @@ class RBACService:
|
|
|
713
856
|
]
|
|
714
857
|
session.bulk_insert_mappings(RolePermission, role_permissions)
|
|
715
858
|
|
|
716
|
-
# Add Casbin policies
|
|
859
|
+
# Add Casbin policies (optimized)
|
|
717
860
|
policies_to_add = [
|
|
718
861
|
[role.uuid, permission.resource, permission.action, permission.module]
|
|
719
862
|
for permission in new_permissions
|
|
720
863
|
]
|
|
721
|
-
self.
|
|
722
|
-
|
|
864
|
+
with self._bulk_operation_context():
|
|
865
|
+
self.enforcer.add_policies(policies_to_add)
|
|
723
866
|
|
|
724
867
|
session.commit()
|
|
725
868
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import Any, List, Optional, Callable
|
|
2
|
+
from sqlalchemy.orm import joinedload
|
|
3
|
+
from contextlib import AbstractContextManager
|
|
4
|
+
from sqlalchemy.orm import Session
|
|
5
|
+
from abs_repository_core.repository.base_repository import BaseRepository
|
|
6
|
+
from abs_repository_core.schemas.base_schema import FindBase, FindUniqueValues
|
|
7
|
+
from abs_auth_rbac_core.models.permissions import Permission
|
|
8
|
+
|
|
9
|
+
class PermissionRepository(BaseRepository):
|
|
10
|
+
def __init__(self, db: Callable[..., Session]):
|
|
11
|
+
self.db = db
|
|
12
|
+
super().__init__(db, Permission)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any, List, Optional, Callable
|
|
2
|
+
from sqlalchemy.orm import joinedload
|
|
3
|
+
from contextlib import AbstractContextManager
|
|
4
|
+
from sqlalchemy.orm import Session
|
|
5
|
+
from abs_repository_core.repository.base_repository import BaseRepository
|
|
6
|
+
from abs_auth_rbac_core.models.roles import Role
|
|
7
|
+
from abs_exception_core.exceptions import NotFoundError
|
|
8
|
+
from abs_repository_core.schemas import FilterSchema, FindBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RoleRepository(BaseRepository):
|
|
12
|
+
def __init__(self, db: Callable[..., Session]):
|
|
13
|
+
self.db = db
|
|
14
|
+
super().__init__(db, Role)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typing import Any, List, Optional
|
|
2
|
+
from abs_repository_core.services.base_service import BaseService
|
|
3
|
+
from abs_repository_core.schemas.base_schema import FindBase, FindUniqueValues
|
|
4
|
+
from abs_auth_rbac_core.models.roles import Role
|
|
5
|
+
from abs_auth_rbac_core.repository.permission_repository import PermissionRepository
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class PermissionService(BaseService):
|
|
10
|
+
def __init__(self, repository:PermissionRepository):
|
|
11
|
+
super().__init__(repository)
|
|
12
|
+
self.repository = repository
|
|
13
|
+
|
|
14
|
+
def list_permissions(self, schema: FindBase, eager: bool = True):
|
|
15
|
+
return self.repository.read_by_options(schema, eager)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Any, List, Optional
|
|
2
|
+
from abs_repository_core.services.base_service import BaseService
|
|
3
|
+
from abs_repository_core.schemas.base_schema import FindBase, FindUniqueValues
|
|
4
|
+
from abs_auth_rbac_core.models.roles import Role
|
|
5
|
+
from abs_auth_rbac_core.repository.role_repository import RoleRepository
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RoleService(BaseService):
|
|
10
|
+
def __init__(self, repository:RoleRepository):
|
|
11
|
+
self.repository = repository
|
|
12
|
+
super().__init__(repository)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def list_roles(self, schema: FindBase, eager: bool = True):
|
|
16
|
+
return self.repository.read_by_options(schema, eager)
|
|
17
|
+
|
|
18
|
+
|
|
@@ -153,6 +153,11 @@ class PermissionAction(str, Enum):
|
|
|
153
153
|
VIEW_IMPORT_DATA = "VIEW_IMPORT_DATA"
|
|
154
154
|
VIEW_WORKFLOW = "VIEW_WORKFLOW"
|
|
155
155
|
VIEW_ERROR_DATA = "VIEW_ERROR_DATA"
|
|
156
|
+
VIEW_ENTITY_RECORD = "VIEW_ENTITY_RECORD"
|
|
157
|
+
EDIT_ENTITY_RECORD = "EDIT_ENTITY_RECORD"
|
|
158
|
+
DELETE_ENTITY_RECORD = "DELETE_ENTITY_RECORD"
|
|
159
|
+
CREATE_ENTITY_RECORD = "CREATE_ENTITY_RECORD"
|
|
160
|
+
CREATE_ENTITY_VIEW = "CREATE_ENTITY_VIEW"
|
|
156
161
|
|
|
157
162
|
|
|
158
163
|
|
|
@@ -172,12 +177,18 @@ class PermissionModule(str, Enum):
|
|
|
172
177
|
ASL = "ASL"
|
|
173
178
|
EDS = "EDS"
|
|
174
179
|
WORKFORCE_AGENT = "WORKFORCE_AGENT"
|
|
175
|
-
|
|
180
|
+
ENTITY = "ENTITY"
|
|
181
|
+
ENTITY_VIEW = "ENTITY_VIEW"
|
|
182
|
+
ENTITY_RECORD = "ENTITY_RECORD"
|
|
183
|
+
CHATBOT = "CHATBOT"
|
|
184
|
+
APPS = "APPS"
|
|
176
185
|
|
|
177
186
|
|
|
178
187
|
class PermissionResource(str, Enum):
|
|
188
|
+
ENTITIES = "ENTITIES"
|
|
179
189
|
DASHBOARD = "DASHBOARDS"
|
|
180
190
|
WORKFORCE_AGENT = "WORKFORCE_AGENT"
|
|
191
|
+
CHATBOT = "CHATBOT"
|
|
181
192
|
EPAR = "EPAR"
|
|
182
193
|
EPARS = "EPARS"
|
|
183
194
|
OCFO = "OCFO"
|
|
@@ -262,6 +273,7 @@ class PermissionResource(str, Enum):
|
|
|
262
273
|
AUTOMATION_ACTION = "AUTOMATION_ACTION"
|
|
263
274
|
AUTOMATION_INPUT = "AUTOMATION_INPUT"
|
|
264
275
|
AUTOMATION_HISTORY = "AUTOMATION_HISTORY"
|
|
276
|
+
USER_PROFILE = "USER_PROFILE"
|
|
265
277
|
|
|
266
278
|
|
|
267
279
|
class PermissionData(NamedTuple):
|
|
@@ -273,6 +285,22 @@ class PermissionData(NamedTuple):
|
|
|
273
285
|
|
|
274
286
|
|
|
275
287
|
class PermissionConstants:
|
|
288
|
+
# User Profile Permissions
|
|
289
|
+
USER_PROFILE_VIEW = PermissionData(
|
|
290
|
+
name="View User Profile",
|
|
291
|
+
description="Permission to view user profile",
|
|
292
|
+
module=PermissionModule.USER_MANAGEMENT,
|
|
293
|
+
resource=PermissionResource.USER_PROFILE,
|
|
294
|
+
action=PermissionAction.VIEW,
|
|
295
|
+
)
|
|
296
|
+
USER_PROFILE_EDIT = PermissionData(
|
|
297
|
+
name="Edit User Profile",
|
|
298
|
+
description="Permission to edit user profile",
|
|
299
|
+
module=PermissionModule.USER_MANAGEMENT,
|
|
300
|
+
resource=PermissionResource.USER_PROFILE,
|
|
301
|
+
action=PermissionAction.EDIT,
|
|
302
|
+
)
|
|
303
|
+
|
|
276
304
|
# Automation Builder Permissions
|
|
277
305
|
AUTOMATION_HISTORY_VIEW = PermissionData(
|
|
278
306
|
name="View Automation History",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: abs-auth-rbac-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.18
|
|
4
4
|
Summary: RBAC and Auth core utilities including JWT token management.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: AutoBridgeSystems
|
|
@@ -11,12 +11,14 @@ Classifier: Programming Language :: Python :: 3
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Requires-Dist: abs-exception-core (>=0.2.
|
|
15
|
-
Requires-Dist: abs-
|
|
14
|
+
Requires-Dist: abs-exception-core (>=0.2.2,<0.3.0)
|
|
15
|
+
Requires-Dist: abs-nosql-repository-core (>=0.11.8,<0.12.0)
|
|
16
|
+
Requires-Dist: abs-repository-core (>=0.3.0,<0.4.0)
|
|
17
|
+
Requires-Dist: abs-utils (>=0.4.3,<0.5.0)
|
|
16
18
|
Requires-Dist: casbin (>=1.41.0,<2.0.0)
|
|
17
19
|
Requires-Dist: casbin-redis-watcher (>=1.3.0,<2.0.0)
|
|
18
20
|
Requires-Dist: casbin-sqlalchemy-adapter (>=1.4.0,<2.0.0)
|
|
19
|
-
Requires-Dist: fastapi[standard] (>=0.115.
|
|
21
|
+
Requires-Dist: fastapi[standard] (>=0.115.2)
|
|
20
22
|
Requires-Dist: passlib (>=1.7.4,<2.0.0)
|
|
21
23
|
Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0)
|
|
22
24
|
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
abs_auth_rbac_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
abs_auth_rbac_core/auth/__init__.py,sha256=
|
|
3
|
-
abs_auth_rbac_core/auth/auth_functions.py,sha256=
|
|
2
|
+
abs_auth_rbac_core/auth/__init__.py,sha256=5PUd5wjIVgdfoD8Zce2uOOE8TtBUhznVHPVwWaiw_9A,312
|
|
3
|
+
abs_auth_rbac_core/auth/auth_functions.py,sha256=p9sOExXOUDXHaP4dccraJ6Xv3RZu2dH4AtRv22yPKXU,1054
|
|
4
4
|
abs_auth_rbac_core/auth/jwt_functions.py,sha256=9vhjWrxXdE8fVQ4FGrPj9y6PoSEsaeFohPhgI-3hToI,4111
|
|
5
|
-
abs_auth_rbac_core/auth/middleware.py,sha256=
|
|
5
|
+
abs_auth_rbac_core/auth/middleware.py,sha256=09gpOdvfgV8QsFEQO46QIRgtTy9-0kPRCVob6RJeE1U,8897
|
|
6
6
|
abs_auth_rbac_core/models/__init__.py,sha256=9ImboxQ04XxRjd5o1RDBn465BOj3F2pahuVXF15NuqE,292
|
|
7
7
|
abs_auth_rbac_core/models/base_model.py,sha256=AaWObslm8sTetv4H1Ia_gPpi_75uF5z1o7Et9WAvstU,612
|
|
8
8
|
abs_auth_rbac_core/models/gov_casbin_rule.py,sha256=9PpCQWg6TWeBvgjRcC2VxSSFMkNW-B9a4e2LmmDmmiY,1000
|
|
@@ -11,17 +11,23 @@ abs_auth_rbac_core/models/rbac_model.py,sha256=GbgMA-lJoU__xYP7wCw2FB9P1ftMzRAU2
|
|
|
11
11
|
abs_auth_rbac_core/models/role_permission.py,sha256=KQ7MGwFvHhXgWL73TGH_elfG0rTLj5Ct11EWi6ypNb8,414
|
|
12
12
|
abs_auth_rbac_core/models/roles.py,sha256=3g52YoCAnVdId4iaQa6Jz1NUTnaZTM_i_4oGF-FneA0,657
|
|
13
13
|
abs_auth_rbac_core/models/seeder/permission_seeder.py,sha256=j-aUy8uLHnUWpMmw1DqXq8yJcqWxQo-D5QjY1S-ifyA,3652
|
|
14
|
-
abs_auth_rbac_core/models/user.py,sha256=
|
|
14
|
+
abs_auth_rbac_core/models/user.py,sha256=rlNXyQ22GKwrg4nIKhpyH-arDIgLOovZr0ZTQNjkLco,1064
|
|
15
15
|
abs_auth_rbac_core/models/user_permission.py,sha256=t1_VUSFyyZAfJK71liDkFkg51yCshCbuRMG9rxFyOro,600
|
|
16
16
|
abs_auth_rbac_core/models/user_role.py,sha256=20pqmtJPzlUrI9ulHGouk8XlFgrGG7I6ikctb8sMUGs,706
|
|
17
17
|
abs_auth_rbac_core/rbac/__init__.py,sha256=oYjtpmfrkEbwWCBAWuRoU1fM4fCpBxkF_lwQrelK1As,79
|
|
18
18
|
abs_auth_rbac_core/rbac/decorator.py,sha256=pEFAW0Nn2iE4KBctPhNOmO_VLeJFDX2V9v2LsCu6kHY,1824
|
|
19
19
|
abs_auth_rbac_core/rbac/policy.conf,sha256=wghhhKxgZH0rPhh1QFrIpq9nevJT3s7OxxvXiU3zzuI,305
|
|
20
|
-
abs_auth_rbac_core/rbac/service.py,sha256=
|
|
20
|
+
abs_auth_rbac_core/rbac/service.py,sha256=rztdSP0wZ-uSv4ASzvxuoHoz3Yng2sYAXSHo-j_-18s,44315
|
|
21
|
+
abs_auth_rbac_core/repository/__init__.py,sha256=tuEdEV5HsePiaEg2Jrakf-QOR3evTeS-2Tq5VqbywyU,154
|
|
22
|
+
abs_auth_rbac_core/repository/permission_repository.py,sha256=SQJyyErrrMnTnLJjhwZythPbYVGt5z0N5GJ5fV6Gvuo,541
|
|
23
|
+
abs_auth_rbac_core/repository/role_repository.py,sha256=OEPpWIm_61rOljPEcejqXyOvowYDK8Uh5K_pvRLfb3Y,562
|
|
21
24
|
abs_auth_rbac_core/schema/__init__.py,sha256=v9xibJ8Wr9k0u6PEYNK0LCGUJD71SB5vxu9BZG0S7tM,46
|
|
22
25
|
abs_auth_rbac_core/schema/permission.py,sha256=XvxPU68FY0PFgkF4GR2bSrzNvFB8c8OgY_d0JOJvMc8,203
|
|
26
|
+
abs_auth_rbac_core/service/__init__.py,sha256=zzzxVCUYYb4heFksjbktWqbST3IcTcTfOMWWC-L5_A0,136
|
|
27
|
+
abs_auth_rbac_core/service/permission_service.py,sha256=tWasmKe0lr1QokmKzjD08O251_ppTnfN9amqVZX_CCU,661
|
|
28
|
+
abs_auth_rbac_core/service/role_service.py,sha256=Q68igKS-cArHaq-tqrjWPpptnrXYImRAEwKQep0ZOBQ,633
|
|
23
29
|
abs_auth_rbac_core/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
abs_auth_rbac_core/util/permission_constants.py,sha256=
|
|
25
|
-
abs_auth_rbac_core-0.3.
|
|
26
|
-
abs_auth_rbac_core-0.3.
|
|
27
|
-
abs_auth_rbac_core-0.3.
|
|
30
|
+
abs_auth_rbac_core/util/permission_constants.py,sha256=P7r6uW6f9YsuxSSsk_oRVN4YqD2CdYvfgnjEmID3czU,103776
|
|
31
|
+
abs_auth_rbac_core-0.3.18.dist-info/METADATA,sha256=5NYRBQI6vSXcu1c7KZl__Q2xgXNFQDyG5mJ7DbPFOzM,23694
|
|
32
|
+
abs_auth_rbac_core-0.3.18.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
33
|
+
abs_auth_rbac_core-0.3.18.dist-info/RECORD,,
|