ipulse-shared-core-ftredge 20.0.1__py3-none-any.whl → 23.1.1__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 ipulse-shared-core-ftredge might be problematic. Click here for more details.
- ipulse_shared_core_ftredge/cache/shared_cache.py +1 -2
- ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py +60 -23
- ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +128 -157
- ipulse_shared_core_ftredge/exceptions/base_exceptions.py +35 -4
- ipulse_shared_core_ftredge/models/__init__.py +3 -7
- ipulse_shared_core_ftredge/models/base_data_model.py +17 -19
- ipulse_shared_core_ftredge/models/catalog/__init__.py +10 -0
- ipulse_shared_core_ftredge/models/catalog/subscriptionplan.py +274 -0
- ipulse_shared_core_ftredge/models/catalog/usertype.py +177 -0
- ipulse_shared_core_ftredge/models/user/__init__.py +5 -0
- ipulse_shared_core_ftredge/models/user/user_permissions.py +66 -0
- ipulse_shared_core_ftredge/models/user/user_subscription.py +348 -0
- ipulse_shared_core_ftredge/models/{user_auth.py → user/userauth.py} +19 -10
- ipulse_shared_core_ftredge/models/{user_profile.py → user/userprofile.py} +53 -21
- ipulse_shared_core_ftredge/models/user/userstatus.py +479 -0
- ipulse_shared_core_ftredge/monitoring/__init__.py +0 -2
- ipulse_shared_core_ftredge/monitoring/tracemon.py +6 -6
- ipulse_shared_core_ftredge/services/__init__.py +11 -13
- ipulse_shared_core_ftredge/services/base/__init__.py +3 -1
- ipulse_shared_core_ftredge/services/base/base_firestore_service.py +77 -16
- ipulse_shared_core_ftredge/services/{cache_aware_firestore_service.py → base/cache_aware_firestore_service.py} +46 -32
- ipulse_shared_core_ftredge/services/catalog/__init__.py +14 -0
- ipulse_shared_core_ftredge/services/catalog/catalog_subscriptionplan_service.py +277 -0
- ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py +376 -0
- ipulse_shared_core_ftredge/services/charging_processors.py +25 -25
- ipulse_shared_core_ftredge/services/user/__init__.py +5 -25
- ipulse_shared_core_ftredge/services/user/user_core_service.py +536 -510
- ipulse_shared_core_ftredge/services/user/user_multistep_operations.py +796 -0
- ipulse_shared_core_ftredge/services/user/user_permissions_operations.py +392 -0
- ipulse_shared_core_ftredge/services/user/user_subscription_operations.py +488 -0
- ipulse_shared_core_ftredge/services/user/userauth_operations.py +928 -0
- ipulse_shared_core_ftredge/services/user/userprofile_operations.py +166 -0
- ipulse_shared_core_ftredge/services/user/userstatus_operations.py +476 -0
- ipulse_shared_core_ftredge/services/{charging_service.py → user_charging_service.py} +9 -9
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/METADATA +3 -4
- ipulse_shared_core_ftredge-23.1.1.dist-info/RECORD +50 -0
- ipulse_shared_core_ftredge/models/subscription.py +0 -190
- ipulse_shared_core_ftredge/models/user_status.py +0 -495
- ipulse_shared_core_ftredge/monitoring/microservmon.py +0 -526
- ipulse_shared_core_ftredge/services/user/iam_management_operations.py +0 -326
- ipulse_shared_core_ftredge/services/user/subscription_management_operations.py +0 -384
- ipulse_shared_core_ftredge/services/user/user_account_operations.py +0 -479
- ipulse_shared_core_ftredge/services/user/user_auth_operations.py +0 -305
- ipulse_shared_core_ftredge/services/user/user_holistic_operations.py +0 -436
- ipulse_shared_core_ftredge-20.0.1.dist-info/RECORD +0 -42
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/WHEEL +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/licenses/LICENCE +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/top_level.txt +0 -0
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
IAM Management Operations - Handle user permissions and access rights
|
|
3
|
-
"""
|
|
4
|
-
import logging
|
|
5
|
-
from typing import Dict, Any, Optional, List
|
|
6
|
-
from datetime import datetime, timezone
|
|
7
|
-
from ...models.user_status import IAMUnitRefAssignment
|
|
8
|
-
from ipulse_shared_base_ftredge.enums.enums_status import ApprovalStatus
|
|
9
|
-
from ipulse_shared_base_ftredge.enums.enums_iam import IAMUnitType
|
|
10
|
-
from ...exceptions import IAMPermissionError, UserAuthError
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class IAMManagementOperations:
|
|
14
|
-
"""
|
|
15
|
-
Handles IAM permissions and access rights management
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
user_account_ops, # UserManagementOperations instance
|
|
21
|
-
logger: Optional[logging.Logger] = None
|
|
22
|
-
):
|
|
23
|
-
self.user_account_ops = user_account_ops
|
|
24
|
-
self.logger = logger or logging.getLogger(__name__)
|
|
25
|
-
|
|
26
|
-
# IAM Permission Operations
|
|
27
|
-
|
|
28
|
-
async def add_user_permission(
|
|
29
|
-
self,
|
|
30
|
-
user_uid: str,
|
|
31
|
-
domain: str,
|
|
32
|
-
permission_name: str,
|
|
33
|
-
iam_unit_type: IAMUnitType,
|
|
34
|
-
source: str,
|
|
35
|
-
expires_at: Optional[datetime] = None,
|
|
36
|
-
updater_uid: Optional[str] = None
|
|
37
|
-
) -> bool:
|
|
38
|
-
"""Add an IAM permission to a user"""
|
|
39
|
-
try:
|
|
40
|
-
user_status = await self.user_account_ops.get_userstatus(user_uid)
|
|
41
|
-
if not user_status:
|
|
42
|
-
raise IAMPermissionError(
|
|
43
|
-
detail="User status not found",
|
|
44
|
-
user_uid=user_uid,
|
|
45
|
-
domain=domain,
|
|
46
|
-
permission=permission_name,
|
|
47
|
-
operation="add_user_permission"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
user_status.add_iam_unit_ref_assignment(
|
|
51
|
-
domain=domain,
|
|
52
|
-
iam_unit_ref=permission_name,
|
|
53
|
-
iam_unit_type=iam_unit_type,
|
|
54
|
-
source=source,
|
|
55
|
-
expires_at=expires_at
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
user_status.updated_at = datetime.now(timezone.utc)
|
|
59
|
-
user_status.updated_by = updater_uid or f"IAMManagement.add_permission:{source}"
|
|
60
|
-
|
|
61
|
-
await self.user_account_ops.update_userstatus(
|
|
62
|
-
user_uid=user_uid,
|
|
63
|
-
status_data=user_status.model_dump(exclude_none=True),
|
|
64
|
-
updater_uid=updater_uid or f"IAMManagement:{source}"
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
self.logger.info(f"Added {iam_unit_type.value} permission '{permission_name}' to user {user_uid} in domain '{domain}'")
|
|
68
|
-
return True
|
|
69
|
-
|
|
70
|
-
except Exception as e:
|
|
71
|
-
self.logger.error(f"Error adding permission to user {user_uid}: {e}", exc_info=True)
|
|
72
|
-
raise IAMPermissionError(
|
|
73
|
-
detail=f"Failed to add permission: {str(e)}",
|
|
74
|
-
user_uid=user_uid,
|
|
75
|
-
domain=domain,
|
|
76
|
-
permission=permission_name,
|
|
77
|
-
operation="add_user_permission",
|
|
78
|
-
original_error=e
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
async def remove_user_permission(
|
|
82
|
-
self,
|
|
83
|
-
user_uid: str,
|
|
84
|
-
domain: str,
|
|
85
|
-
permission_name: str,
|
|
86
|
-
iam_unit_type: IAMUnitType,
|
|
87
|
-
updater_uid: Optional[str] = None
|
|
88
|
-
) -> bool:
|
|
89
|
-
"""Remove an IAM permission from a user"""
|
|
90
|
-
try:
|
|
91
|
-
user_status = await self.user_account_ops.get_userstatus(user_uid)
|
|
92
|
-
if not user_status:
|
|
93
|
-
raise IAMPermissionError(
|
|
94
|
-
detail="User status not found",
|
|
95
|
-
user_uid=user_uid,
|
|
96
|
-
domain=domain,
|
|
97
|
-
permission=permission_name,
|
|
98
|
-
operation="remove_user_permission"
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
# Check if permission exists
|
|
102
|
-
if (domain not in user_status.iam_domain_permissions or
|
|
103
|
-
iam_unit_type.value not in user_status.iam_domain_permissions[domain] or
|
|
104
|
-
permission_name not in user_status.iam_domain_permissions[domain][iam_unit_type.value]):
|
|
105
|
-
self.logger.warning(f"Permission '{permission_name}' not found for user {user_uid} in domain '{domain}'")
|
|
106
|
-
return False
|
|
107
|
-
|
|
108
|
-
# Remove the permission
|
|
109
|
-
del user_status.iam_domain_permissions[domain][iam_unit_type.value][permission_name]
|
|
110
|
-
|
|
111
|
-
# Clean up empty structures
|
|
112
|
-
if not user_status.iam_domain_permissions[domain][iam_unit_type.value]:
|
|
113
|
-
del user_status.iam_domain_permissions[domain][iam_unit_type.value]
|
|
114
|
-
if not user_status.iam_domain_permissions[domain]:
|
|
115
|
-
del user_status.iam_domain_permissions[domain]
|
|
116
|
-
|
|
117
|
-
user_status.updated_at = datetime.now(timezone.utc)
|
|
118
|
-
user_status.updated_by = updater_uid or "IAMManagement.remove_permission"
|
|
119
|
-
|
|
120
|
-
await self.user_account_ops.update_userstatus(
|
|
121
|
-
user_uid=user_uid,
|
|
122
|
-
status_data=user_status.model_dump(exclude_none=True),
|
|
123
|
-
updater_uid=updater_uid or "IAMManagement"
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
self.logger.info(f"Removed {iam_unit_type.value} permission '{permission_name}' from user {user_uid} in domain '{domain}'")
|
|
127
|
-
return True
|
|
128
|
-
|
|
129
|
-
except Exception as e:
|
|
130
|
-
self.logger.error(f"Error removing permission from user {user_uid}: {e}", exc_info=True)
|
|
131
|
-
raise IAMPermissionError(
|
|
132
|
-
detail=f"Failed to remove permission: {str(e)}",
|
|
133
|
-
user_uid=user_uid,
|
|
134
|
-
domain=domain,
|
|
135
|
-
permission=permission_name,
|
|
136
|
-
operation="remove_user_permission",
|
|
137
|
-
original_error=e
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
async def get_user_permissions(
|
|
141
|
-
self,
|
|
142
|
-
user_uid: str,
|
|
143
|
-
domain: Optional[str] = None,
|
|
144
|
-
iam_unit_type: Optional[IAMUnitType] = None,
|
|
145
|
-
include_expired: bool = False
|
|
146
|
-
) -> Dict[str, Dict[str, Dict[str, IAMUnitRefAssignment]]]:
|
|
147
|
-
"""Get user's IAM permissions with optional filtering"""
|
|
148
|
-
try:
|
|
149
|
-
user_status = await self.user_account_ops.get_userstatus(user_uid)
|
|
150
|
-
if not user_status:
|
|
151
|
-
return {}
|
|
152
|
-
|
|
153
|
-
permissions = user_status.iam_domain_permissions
|
|
154
|
-
|
|
155
|
-
# Filter by domain if specified
|
|
156
|
-
if domain:
|
|
157
|
-
permissions = {domain: permissions.get(domain, {})}
|
|
158
|
-
|
|
159
|
-
# Filter by IAM unit type if specified
|
|
160
|
-
if iam_unit_type:
|
|
161
|
-
filtered_permissions = {}
|
|
162
|
-
for dom, domain_perms in permissions.items():
|
|
163
|
-
if iam_unit_type.value in domain_perms:
|
|
164
|
-
filtered_permissions[dom] = {iam_unit_type.value: domain_perms[iam_unit_type.value]}
|
|
165
|
-
permissions = filtered_permissions
|
|
166
|
-
|
|
167
|
-
# Filter expired permissions if requested
|
|
168
|
-
if not include_expired:
|
|
169
|
-
filtered_permissions = {}
|
|
170
|
-
for dom, domain_perms in permissions.items():
|
|
171
|
-
filtered_domain = {}
|
|
172
|
-
for unit_type, unit_perms in domain_perms.items():
|
|
173
|
-
filtered_unit = {
|
|
174
|
-
perm_name: assignment
|
|
175
|
-
for perm_name, assignment in unit_perms.items()
|
|
176
|
-
if assignment.is_valid()
|
|
177
|
-
}
|
|
178
|
-
if filtered_unit:
|
|
179
|
-
filtered_domain[unit_type] = filtered_unit
|
|
180
|
-
if filtered_domain:
|
|
181
|
-
filtered_permissions[dom] = filtered_domain
|
|
182
|
-
permissions = filtered_permissions
|
|
183
|
-
|
|
184
|
-
return permissions
|
|
185
|
-
|
|
186
|
-
except Exception as e:
|
|
187
|
-
self.logger.error(f"Error getting permissions for user {user_uid}: {e}", exc_info=True)
|
|
188
|
-
raise IAMPermissionError(
|
|
189
|
-
detail=f"Failed to get user permissions: {str(e)}",
|
|
190
|
-
user_uid=user_uid,
|
|
191
|
-
domain=domain,
|
|
192
|
-
operation="get_user_permissions",
|
|
193
|
-
original_error=e
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
async def has_user_permission(
|
|
197
|
-
self,
|
|
198
|
-
user_uid: str,
|
|
199
|
-
domain: str,
|
|
200
|
-
permission_name: str,
|
|
201
|
-
iam_unit_type: IAMUnitType = IAMUnitType.GROUPS
|
|
202
|
-
) -> bool:
|
|
203
|
-
"""Check if user has a specific permission"""
|
|
204
|
-
try:
|
|
205
|
-
permissions = await self.get_user_permissions(
|
|
206
|
-
user_uid=user_uid,
|
|
207
|
-
domain=domain,
|
|
208
|
-
iam_unit_type=iam_unit_type,
|
|
209
|
-
include_expired=False
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
return (domain in permissions and
|
|
213
|
-
iam_unit_type.value in permissions[domain] and
|
|
214
|
-
permission_name in permissions[domain][iam_unit_type.value])
|
|
215
|
-
|
|
216
|
-
except Exception as e:
|
|
217
|
-
self.logger.error(f"Error checking permission for user {user_uid}: {e}", exc_info=True)
|
|
218
|
-
return False
|
|
219
|
-
|
|
220
|
-
async def cleanup_expired_permissions(self, user_uid: str, updater_uid: Optional[str] = None) -> int:
|
|
221
|
-
"""Remove all expired permissions for a user"""
|
|
222
|
-
try:
|
|
223
|
-
user_status = await self.user_account_ops.get_userstatus(user_uid)
|
|
224
|
-
if not user_status:
|
|
225
|
-
return 0
|
|
226
|
-
|
|
227
|
-
removed_count = user_status.remove_expired_iam_unit_refs()
|
|
228
|
-
|
|
229
|
-
if removed_count > 0:
|
|
230
|
-
user_status.updated_at = datetime.now(timezone.utc)
|
|
231
|
-
user_status.updated_by = updater_uid or "IAMManagement.cleanup_expired"
|
|
232
|
-
|
|
233
|
-
await self.user_account_ops.update_userstatus(
|
|
234
|
-
user_uid=user_uid,
|
|
235
|
-
status_data=user_status.model_dump(exclude_none=True),
|
|
236
|
-
updater_uid=updater_uid or "IAMManagement"
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
self.logger.info(f"Cleaned up {removed_count} expired permissions for user {user_uid}")
|
|
240
|
-
|
|
241
|
-
return removed_count
|
|
242
|
-
|
|
243
|
-
except Exception as e:
|
|
244
|
-
self.logger.error(f"Error cleaning up expired permissions for user {user_uid}: {e}", exc_info=True)
|
|
245
|
-
raise IAMPermissionError(
|
|
246
|
-
detail=f"Failed to cleanup expired permissions: {str(e)}",
|
|
247
|
-
user_uid=user_uid,
|
|
248
|
-
operation="cleanup_expired_permissions",
|
|
249
|
-
original_error=e
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
# Legacy group methods for backward compatibility
|
|
253
|
-
|
|
254
|
-
async def add_user_group(
|
|
255
|
-
self,
|
|
256
|
-
user_uid: str,
|
|
257
|
-
domain: str,
|
|
258
|
-
group_name: str,
|
|
259
|
-
source: str,
|
|
260
|
-
expires_at: Optional[datetime] = None,
|
|
261
|
-
updater_uid: Optional[str] = None
|
|
262
|
-
) -> bool:
|
|
263
|
-
"""Add a group to a user (legacy method)"""
|
|
264
|
-
return await self.add_user_permission(
|
|
265
|
-
user_uid=user_uid,
|
|
266
|
-
domain=domain,
|
|
267
|
-
permission_name=group_name,
|
|
268
|
-
iam_unit_type=IAMUnitType.GROUPS,
|
|
269
|
-
source=source,
|
|
270
|
-
expires_at=expires_at,
|
|
271
|
-
updater_uid=updater_uid
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
async def remove_user_group(
|
|
275
|
-
self,
|
|
276
|
-
user_uid: str,
|
|
277
|
-
domain: str,
|
|
278
|
-
group_name: str,
|
|
279
|
-
updater_uid: Optional[str] = None
|
|
280
|
-
) -> bool:
|
|
281
|
-
"""Remove a group from a user (legacy method)"""
|
|
282
|
-
return await self.remove_user_permission(
|
|
283
|
-
user_uid=user_uid,
|
|
284
|
-
domain=domain,
|
|
285
|
-
permission_name=group_name,
|
|
286
|
-
iam_unit_type=IAMUnitType.GROUPS,
|
|
287
|
-
updater_uid=updater_uid
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
async def get_user_groups(
|
|
291
|
-
self,
|
|
292
|
-
user_uid: str,
|
|
293
|
-
domain: Optional[str] = None,
|
|
294
|
-
include_expired: bool = False
|
|
295
|
-
) -> Dict[str, Dict[str, IAMUnitRefAssignment]]:
|
|
296
|
-
"""Get user's groups (legacy method)"""
|
|
297
|
-
permissions = await self.get_user_permissions(
|
|
298
|
-
user_uid=user_uid,
|
|
299
|
-
domain=domain,
|
|
300
|
-
iam_unit_type=IAMUnitType.GROUPS,
|
|
301
|
-
include_expired=include_expired
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
# Flatten to match legacy format
|
|
305
|
-
groups = {}
|
|
306
|
-
for domain_name, domain_perms in permissions.items():
|
|
307
|
-
if IAMUnitType.GROUPS.value in domain_perms:
|
|
308
|
-
groups[domain_name] = domain_perms[IAMUnitType.GROUPS.value]
|
|
309
|
-
|
|
310
|
-
return groups
|
|
311
|
-
|
|
312
|
-
async def has_user_group(
|
|
313
|
-
self,
|
|
314
|
-
user_uid: str,
|
|
315
|
-
domain: str,
|
|
316
|
-
group_name: str
|
|
317
|
-
) -> bool:
|
|
318
|
-
"""Check if user has a specific group (legacy method)"""
|
|
319
|
-
return await self.has_user_permission(
|
|
320
|
-
user_uid=user_uid,
|
|
321
|
-
domain=domain,
|
|
322
|
-
permission_name=group_name,
|
|
323
|
-
iam_unit_type=IAMUnitType.GROUPS
|
|
324
|
-
)
|
|
325
|
-
|
|
326
|
-
|