ipulse-shared-core-ftredge 20.0.1__py3-none-any.whl → 22.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/authz_for_apis.py +4 -4
- ipulse_shared_core_ftredge/exceptions/base_exceptions.py +23 -0
- 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 +273 -0
- ipulse_shared_core_ftredge/models/catalog/usertype.py +170 -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/{subscription.py → user/user_subscription.py} +66 -20
- 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 +430 -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 +73 -14
- 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 +273 -0
- ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py +307 -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/firebase_auth_admin_helpers.py +160 -0
- ipulse_shared_core_ftredge/services/user/user_core_service.py +423 -515
- ipulse_shared_core_ftredge/services/user/user_multistep_operations.py +726 -0
- ipulse_shared_core_ftredge/services/user/user_permissions_operations.py +392 -0
- ipulse_shared_core_ftredge/services/user/user_subscription_operations.py +484 -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 +212 -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-22.1.1.dist-info}/METADATA +3 -4
- ipulse_shared_core_ftredge-22.1.1.dist-info/RECORD +51 -0
- 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-22.1.1.dist-info}/WHEEL +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/licenses/LICENCE +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-22.1.1.dist-info}/top_level.txt +0 -0
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
User Auth Operations - Handle Firebase Auth user creation, management, and deletion
|
|
3
|
-
"""
|
|
4
|
-
import os
|
|
5
|
-
import asyncio
|
|
6
|
-
import logging
|
|
7
|
-
from typing import Dict, Any, Optional, List
|
|
8
|
-
from firebase_admin import auth
|
|
9
|
-
from ...models.user_auth import UserAuth
|
|
10
|
-
from ...exceptions import UserAuthError
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class UserAuthOperations:
|
|
14
|
-
"""
|
|
15
|
-
Handles Firebase Auth operations for user creation, management, and deletion
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
def __init__(self, logger: Optional[logging.Logger] = None):
|
|
19
|
-
self.logger = logger or logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
# User Auth Operations
|
|
22
|
-
|
|
23
|
-
async def create_userauth(
|
|
24
|
-
self,
|
|
25
|
-
user_auth: UserAuth
|
|
26
|
-
) -> str:
|
|
27
|
-
"""Create a new Firebase Auth user from UserAuth model and return the UID"""
|
|
28
|
-
try:
|
|
29
|
-
# Create user synchronously
|
|
30
|
-
loop = asyncio.get_event_loop()
|
|
31
|
-
user_record = await loop.run_in_executor(
|
|
32
|
-
None,
|
|
33
|
-
lambda: auth.create_user(
|
|
34
|
-
email=user_auth.email,
|
|
35
|
-
email_verified=user_auth.email_verified,
|
|
36
|
-
password=user_auth.password,
|
|
37
|
-
phone_number=user_auth.phone_number,
|
|
38
|
-
disabled=user_auth.disabled
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
user_uid = user_record.uid
|
|
43
|
-
self.logger.info(f"Successfully created Firebase Auth user with UID: {user_uid}")
|
|
44
|
-
|
|
45
|
-
# Set custom claims if provided
|
|
46
|
-
if user_auth.custom_claims:
|
|
47
|
-
await self.set_userauth_custom_claims(user_uid, user_auth.custom_claims)
|
|
48
|
-
|
|
49
|
-
return user_uid
|
|
50
|
-
|
|
51
|
-
except auth.EmailAlreadyExistsError:
|
|
52
|
-
raise UserAuthError(
|
|
53
|
-
detail=f"User with email {user_auth.email} already exists",
|
|
54
|
-
operation="create_userauth",
|
|
55
|
-
additional_info={"email": str(user_auth.email)}
|
|
56
|
-
)
|
|
57
|
-
except Exception as e:
|
|
58
|
-
self.logger.error(f"Failed to create Firebase Auth user: {e}", exc_info=True)
|
|
59
|
-
raise UserAuthError(
|
|
60
|
-
detail=f"Failed to create Firebase Auth user: {str(e)}",
|
|
61
|
-
operation="create_userauth",
|
|
62
|
-
additional_info={"email": str(user_auth.email)},
|
|
63
|
-
original_error=e
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
# Firebase Auth User Management
|
|
67
|
-
|
|
68
|
-
async def get_userauth(self, user_uid: str) -> Optional[auth.UserRecord]:
|
|
69
|
-
"""Get Firebase Auth user by UID"""
|
|
70
|
-
try:
|
|
71
|
-
loop = asyncio.get_event_loop()
|
|
72
|
-
user_record = await loop.run_in_executor(
|
|
73
|
-
None,
|
|
74
|
-
auth.get_user,
|
|
75
|
-
user_uid
|
|
76
|
-
)
|
|
77
|
-
return user_record
|
|
78
|
-
except auth.UserNotFoundError:
|
|
79
|
-
return None
|
|
80
|
-
except Exception as e:
|
|
81
|
-
self.logger.error(f"Failed to get Firebase Auth user {user_uid}: {e}", exc_info=True)
|
|
82
|
-
raise UserAuthError(
|
|
83
|
-
detail=f"Failed to get Firebase Auth user: {str(e)}",
|
|
84
|
-
user_uid=user_uid,
|
|
85
|
-
operation="get_userauth",
|
|
86
|
-
original_error=e
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
async def get_userauth_by_email(self, email: str) -> Optional[auth.UserRecord]:
|
|
90
|
-
"""Get Firebase Auth user by email"""
|
|
91
|
-
try:
|
|
92
|
-
loop = asyncio.get_event_loop()
|
|
93
|
-
user_record = await loop.run_in_executor(
|
|
94
|
-
None,
|
|
95
|
-
auth.get_user_by_email,
|
|
96
|
-
email
|
|
97
|
-
)
|
|
98
|
-
return user_record
|
|
99
|
-
except auth.UserNotFoundError:
|
|
100
|
-
return None
|
|
101
|
-
except Exception as e:
|
|
102
|
-
self.logger.error(f"Failed to get Firebase Auth user by email {email}: {e}", exc_info=True)
|
|
103
|
-
raise UserAuthError(
|
|
104
|
-
detail=f"Failed to get Firebase Auth user by email: {str(e)}",
|
|
105
|
-
operation="get_userauth_by_email",
|
|
106
|
-
additional_info={"email": email},
|
|
107
|
-
original_error=e
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
async def update_userauth(
|
|
111
|
-
self,
|
|
112
|
-
user_uid: str,
|
|
113
|
-
email: Optional[str] = None,
|
|
114
|
-
password: Optional[str] = None,
|
|
115
|
-
display_name: Optional[str] = None,
|
|
116
|
-
phone_number: Optional[str] = None,
|
|
117
|
-
email_verified: Optional[bool] = None,
|
|
118
|
-
disabled: Optional[bool] = None
|
|
119
|
-
) -> auth.UserRecord:
|
|
120
|
-
"""Update Firebase Auth user"""
|
|
121
|
-
try:
|
|
122
|
-
loop = asyncio.get_event_loop()
|
|
123
|
-
user_record = await loop.run_in_executor(
|
|
124
|
-
None,
|
|
125
|
-
lambda: auth.update_user(
|
|
126
|
-
uid=user_uid,
|
|
127
|
-
email=email,
|
|
128
|
-
password=password,
|
|
129
|
-
display_name=display_name,
|
|
130
|
-
phone_number=phone_number,
|
|
131
|
-
email_verified=email_verified,
|
|
132
|
-
disabled=disabled
|
|
133
|
-
)
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
self.logger.info(f"Successfully updated Firebase Auth user: {user_uid}")
|
|
137
|
-
return user_record
|
|
138
|
-
|
|
139
|
-
except auth.UserNotFoundError:
|
|
140
|
-
raise UserAuthError(
|
|
141
|
-
detail=f"Firebase Auth user not found",
|
|
142
|
-
user_uid=user_uid,
|
|
143
|
-
operation="update_userauth"
|
|
144
|
-
)
|
|
145
|
-
except Exception as e:
|
|
146
|
-
self.logger.error(f"Failed to update Firebase Auth user {user_uid}: {e}", exc_info=True)
|
|
147
|
-
raise UserAuthError(
|
|
148
|
-
detail=f"Failed to update Firebase Auth user: {str(e)}",
|
|
149
|
-
user_uid=user_uid,
|
|
150
|
-
operation="update_userauth",
|
|
151
|
-
original_error=e
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# Firebase Auth Custom Claims
|
|
155
|
-
|
|
156
|
-
def generate_firebase_custom_claims(
|
|
157
|
-
self,
|
|
158
|
-
primary_usertype: str,
|
|
159
|
-
secondary_usertypes: Optional[List[str]] = None,
|
|
160
|
-
organizations_uids: Optional[List[str]] = None,
|
|
161
|
-
user_approval_status: str = "pending"
|
|
162
|
-
) -> Dict[str, Any]:
|
|
163
|
-
"""Generate Firebase custom claims with minimal required fields"""
|
|
164
|
-
try:
|
|
165
|
-
claims = {
|
|
166
|
-
"primary_usertype": primary_usertype,
|
|
167
|
-
"secondary_usertypes": secondary_usertypes or [],
|
|
168
|
-
"organizations_uids": organizations_uids or [],
|
|
169
|
-
"user_approval_status": user_approval_status,
|
|
170
|
-
}
|
|
171
|
-
self.logger.info(f"Generated Firebase custom claims for usertype {primary_usertype}")
|
|
172
|
-
return claims
|
|
173
|
-
except Exception as e:
|
|
174
|
-
self.logger.error(f"Error generating Firebase custom claims: {e}", exc_info=True)
|
|
175
|
-
raise UserAuthError(
|
|
176
|
-
detail=f"Failed to generate Firebase custom claims: {str(e)}",
|
|
177
|
-
operation="generate_firebase_custom_claims",
|
|
178
|
-
original_error=e
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
async def set_userauth_custom_claims(
|
|
182
|
-
self,
|
|
183
|
-
user_uid: str,
|
|
184
|
-
custom_claims: Dict[str, Any],
|
|
185
|
-
merge_with_existing: bool = False
|
|
186
|
-
) -> bool:
|
|
187
|
-
"""Set Firebase Auth custom claims for a user with optional merging"""
|
|
188
|
-
try:
|
|
189
|
-
if merge_with_existing:
|
|
190
|
-
# Get existing claims and merge
|
|
191
|
-
user_record = await self.get_userauth(user_uid)
|
|
192
|
-
if user_record and user_record.custom_claims:
|
|
193
|
-
existing_claims = user_record.custom_claims.copy()
|
|
194
|
-
existing_claims.update(custom_claims)
|
|
195
|
-
custom_claims = existing_claims
|
|
196
|
-
|
|
197
|
-
loop = asyncio.get_event_loop()
|
|
198
|
-
await loop.run_in_executor(
|
|
199
|
-
None,
|
|
200
|
-
auth.set_custom_user_claims,
|
|
201
|
-
user_uid,
|
|
202
|
-
custom_claims
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
self.logger.info(f"Successfully set Firebase custom claims for user: {user_uid}")
|
|
206
|
-
return True
|
|
207
|
-
|
|
208
|
-
except auth.UserNotFoundError:
|
|
209
|
-
raise UserAuthError(
|
|
210
|
-
detail=f"Firebase Auth user not found",
|
|
211
|
-
user_uid=user_uid,
|
|
212
|
-
operation="set_userauth_custom_claims"
|
|
213
|
-
)
|
|
214
|
-
except Exception as e:
|
|
215
|
-
self.logger.error(f"Failed to set Firebase custom claims for user {user_uid}: {e}", exc_info=True)
|
|
216
|
-
raise UserAuthError(
|
|
217
|
-
detail=f"Failed to set Firebase custom claims: {str(e)}",
|
|
218
|
-
user_uid=user_uid,
|
|
219
|
-
operation="set_userauth_custom_claims",
|
|
220
|
-
original_error=e
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
async def get_userauth_custom_claims(self, user_uid: str) -> Optional[Dict[str, Any]]:
|
|
224
|
-
"""Get Firebase Auth custom claims for a user"""
|
|
225
|
-
try:
|
|
226
|
-
user_record = await self.get_userauth(user_uid)
|
|
227
|
-
return user_record.custom_claims if user_record else None
|
|
228
|
-
except Exception as e:
|
|
229
|
-
self.logger.error(f"Failed to get Firebase custom claims for user {user_uid}: {e}", exc_info=True)
|
|
230
|
-
raise UserAuthError(
|
|
231
|
-
detail=f"Failed to get Firebase custom claims: {str(e)}",
|
|
232
|
-
user_uid=user_uid,
|
|
233
|
-
operation="get_userauth_custom_claims",
|
|
234
|
-
original_error=e
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
# Firebase Auth User Deletion
|
|
238
|
-
|
|
239
|
-
async def delete_userauth(self, user_uid: str) -> bool:
|
|
240
|
-
"""Delete Firebase Auth user"""
|
|
241
|
-
try:
|
|
242
|
-
loop = asyncio.get_event_loop()
|
|
243
|
-
await loop.run_in_executor(
|
|
244
|
-
None,
|
|
245
|
-
auth.delete_user,
|
|
246
|
-
user_uid
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
self.logger.info(f"Successfully deleted Firebase Auth user: {user_uid}")
|
|
250
|
-
return True
|
|
251
|
-
|
|
252
|
-
except auth.UserNotFoundError:
|
|
253
|
-
self.logger.warning(f"Firebase Auth user {user_uid} not found during deletion")
|
|
254
|
-
return True # Consider non-existent user as successfully deleted
|
|
255
|
-
except Exception as e:
|
|
256
|
-
self.logger.error(f"Failed to delete Firebase Auth user {user_uid}: {e}", exc_info=True)
|
|
257
|
-
raise UserAuthError(
|
|
258
|
-
detail=f"Failed to delete Firebase Auth user: {str(e)}",
|
|
259
|
-
user_uid=user_uid,
|
|
260
|
-
operation="delete_userauth",
|
|
261
|
-
original_error=e
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
# Utility Methods
|
|
265
|
-
|
|
266
|
-
async def userauth_exists(self, user_uid: str) -> bool:
|
|
267
|
-
"""Check if Firebase Auth user exists"""
|
|
268
|
-
user_record = await self.get_userauth(user_uid)
|
|
269
|
-
return user_record is not None
|
|
270
|
-
|
|
271
|
-
async def userauth_exists_by_email(self, email: str) -> bool:
|
|
272
|
-
"""Check if Firebase Auth user exists by email"""
|
|
273
|
-
user_record = await self.get_userauth_by_email(email)
|
|
274
|
-
return user_record is not None
|
|
275
|
-
|
|
276
|
-
async def validate_userauth(self, user_uid: str) -> bool:
|
|
277
|
-
"""Validate that Firebase Auth user exists and is not disabled"""
|
|
278
|
-
try:
|
|
279
|
-
user_record = await self.get_userauth(user_uid)
|
|
280
|
-
if not user_record:
|
|
281
|
-
return False
|
|
282
|
-
return not user_record.disabled
|
|
283
|
-
except Exception:
|
|
284
|
-
return False
|
|
285
|
-
|
|
286
|
-
async def list_userauth(
|
|
287
|
-
self,
|
|
288
|
-
page_token: Optional[str] = None,
|
|
289
|
-
max_results: int = 1000
|
|
290
|
-
) -> auth.ListUsersPage:
|
|
291
|
-
"""List Firebase Auth users with pagination"""
|
|
292
|
-
try:
|
|
293
|
-
loop = asyncio.get_event_loop()
|
|
294
|
-
page = await loop.run_in_executor(
|
|
295
|
-
None,
|
|
296
|
-
lambda: auth.list_users(page_token=page_token, max_results=max_results)
|
|
297
|
-
)
|
|
298
|
-
return page
|
|
299
|
-
except Exception as e:
|
|
300
|
-
self.logger.error(f"Failed to list Firebase Auth users: {e}", exc_info=True)
|
|
301
|
-
raise UserAuthError(
|
|
302
|
-
detail=f"Failed to list Firebase Auth users: {str(e)}",
|
|
303
|
-
operation="list_userauth",
|
|
304
|
-
original_error=e
|
|
305
|
-
)
|