square-authentication 10.0.0__py3-none-any.whl → 10.0.2__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.
- square_authentication/routes/core.py +166 -3002
- square_authentication/routes/profile.py +38 -579
- square_authentication/routes/utility.py +7 -37
- square_authentication/utils/routes/__init__.py +0 -0
- square_authentication/utils/routes/core.py +3251 -0
- square_authentication/utils/routes/profile.py +647 -0
- square_authentication/utils/routes/utility.py +55 -0
- {square_authentication-10.0.0.dist-info → square_authentication-10.0.2.dist-info}/METADATA +7 -2
- {square_authentication-10.0.0.dist-info → square_authentication-10.0.2.dist-info}/RECORD +12 -8
- {square_authentication-10.0.0.dist-info → square_authentication-10.0.2.dist-info}/WHEEL +0 -0
- {square_authentication-10.0.0.dist-info → square_authentication-10.0.2.dist-info}/licenses/LICENSE +0 -0
- {square_authentication-10.0.0.dist-info → square_authentication-10.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3251 @@
|
|
1
|
+
import copy
|
2
|
+
import io
|
3
|
+
import mimetypes
|
4
|
+
import random
|
5
|
+
import re
|
6
|
+
import uuid
|
7
|
+
from datetime import datetime, timedelta, timezone
|
8
|
+
|
9
|
+
import bcrypt
|
10
|
+
import jwt
|
11
|
+
from fastapi import HTTPException, status
|
12
|
+
from fastapi.responses import JSONResponse
|
13
|
+
from google.auth.transport import requests as google_requests
|
14
|
+
from google.oauth2 import id_token
|
15
|
+
from requests import HTTPError
|
16
|
+
from square_commons import get_api_output_in_standard_format, send_email_using_mailgun
|
17
|
+
from square_commons.api_utils import make_request
|
18
|
+
from square_database_helper.pydantic_models import FilterConditionsV0, FiltersV0
|
19
|
+
from square_database_structure.square import global_string_database_name
|
20
|
+
from square_database_structure.square.authentication import global_string_schema_name
|
21
|
+
from square_database_structure.square.authentication.enums import (
|
22
|
+
RecoveryMethodEnum,
|
23
|
+
AuthProviderEnum,
|
24
|
+
VerificationCodeTypeEnum,
|
25
|
+
)
|
26
|
+
from square_database_structure.square.authentication.tables import (
|
27
|
+
User,
|
28
|
+
UserApp,
|
29
|
+
UserCredential,
|
30
|
+
UserSession,
|
31
|
+
UserProfile,
|
32
|
+
UserRecoveryMethod,
|
33
|
+
UserAuthProvider,
|
34
|
+
UserVerificationCode,
|
35
|
+
)
|
36
|
+
from square_database_structure.square.email import (
|
37
|
+
global_string_schema_name as email_schema_name,
|
38
|
+
)
|
39
|
+
from square_database_structure.square.email.enums import EmailTypeEnum, EmailStatusEnum
|
40
|
+
from square_database_structure.square.email.tables import EmailLog
|
41
|
+
from square_database_structure.square.public import (
|
42
|
+
global_string_schema_name as global_string_public_schema_name,
|
43
|
+
)
|
44
|
+
from square_database_structure.square.public.tables import App
|
45
|
+
|
46
|
+
from square_authentication.configuration import (
|
47
|
+
GOOGLE_AUTH_PLATFORM_CLIENT_ID,
|
48
|
+
)
|
49
|
+
from square_authentication.configuration import (
|
50
|
+
config_int_access_token_valid_minutes,
|
51
|
+
config_int_refresh_token_valid_minutes,
|
52
|
+
config_str_secret_key_for_access_token,
|
53
|
+
config_str_secret_key_for_refresh_token,
|
54
|
+
global_object_square_logger,
|
55
|
+
global_object_square_database_helper,
|
56
|
+
MAIL_GUN_API_KEY,
|
57
|
+
NUMBER_OF_RECOVERY_CODES,
|
58
|
+
NUMBER_OF_DIGITS_IN_EMAIL_PASSWORD_RESET_CODE,
|
59
|
+
EXPIRY_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS,
|
60
|
+
global_object_square_file_store_helper,
|
61
|
+
RESEND_COOL_DOWN_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS,
|
62
|
+
)
|
63
|
+
from square_authentication.messages import messages
|
64
|
+
from square_authentication.pydantic_models.core import (
|
65
|
+
TokenType,
|
66
|
+
)
|
67
|
+
from square_authentication.utils.core import generate_default_username_for_google_users
|
68
|
+
from square_authentication.utils.token import get_jwt_payload
|
69
|
+
|
70
|
+
|
71
|
+
@global_object_square_logger.auto_logger()
|
72
|
+
def util_register_username_v0(username, password, app_id):
|
73
|
+
local_str_user_id = None
|
74
|
+
local_str_access_token = None
|
75
|
+
local_str_refresh_token = None
|
76
|
+
local_object_refresh_token_expiry_time = None
|
77
|
+
username = username.lower()
|
78
|
+
try:
|
79
|
+
"""
|
80
|
+
validation
|
81
|
+
"""
|
82
|
+
# validation for username
|
83
|
+
username_pattern = re.compile(r"^[a-z0-9._-]{2,20}$")
|
84
|
+
if not username_pattern.match(username):
|
85
|
+
output_content = get_api_output_in_standard_format(
|
86
|
+
message=messages["USERNAME_INVALID"],
|
87
|
+
log=f"username '{username}' is invalid. it must start and end with a letter, "
|
88
|
+
f"contain only lowercase letters, numbers, underscores, or hyphens, "
|
89
|
+
f"and not have consecutive separators.",
|
90
|
+
)
|
91
|
+
raise HTTPException(
|
92
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
93
|
+
detail=output_content,
|
94
|
+
)
|
95
|
+
local_list_response_user_creds = (
|
96
|
+
global_object_square_database_helper.get_rows_v0(
|
97
|
+
database_name=global_string_database_name,
|
98
|
+
schema_name=global_string_schema_name,
|
99
|
+
table_name=User.__tablename__,
|
100
|
+
filters=FiltersV0(
|
101
|
+
root={User.user_username.name: FilterConditionsV0(eq=username)}
|
102
|
+
),
|
103
|
+
)["data"]["main"]
|
104
|
+
)
|
105
|
+
if len(local_list_response_user_creds) > 0:
|
106
|
+
output_content = get_api_output_in_standard_format(
|
107
|
+
message=messages["USERNAME_ALREADY_EXISTS"],
|
108
|
+
log=f"an account with the username {username} already exists.",
|
109
|
+
)
|
110
|
+
raise HTTPException(
|
111
|
+
status_code=status.HTTP_409_CONFLICT,
|
112
|
+
detail=output_content,
|
113
|
+
)
|
114
|
+
|
115
|
+
"""
|
116
|
+
main process
|
117
|
+
"""
|
118
|
+
# entry in user table
|
119
|
+
local_list_response_user = global_object_square_database_helper.insert_rows_v0(
|
120
|
+
data=[
|
121
|
+
{
|
122
|
+
User.user_username.name: username,
|
123
|
+
}
|
124
|
+
],
|
125
|
+
database_name=global_string_database_name,
|
126
|
+
schema_name=global_string_schema_name,
|
127
|
+
table_name=User.__tablename__,
|
128
|
+
)["data"]["main"]
|
129
|
+
local_str_user_id = local_list_response_user[0][User.user_id.name]
|
130
|
+
|
131
|
+
# entry in user auth provider table
|
132
|
+
global_object_square_database_helper.insert_rows_v0(
|
133
|
+
data=[
|
134
|
+
{
|
135
|
+
UserAuthProvider.user_id.name: local_str_user_id,
|
136
|
+
UserAuthProvider.auth_provider.name: AuthProviderEnum.SELF.value,
|
137
|
+
}
|
138
|
+
],
|
139
|
+
database_name=global_string_database_name,
|
140
|
+
schema_name=global_string_schema_name,
|
141
|
+
table_name=UserAuthProvider.__tablename__,
|
142
|
+
)
|
143
|
+
|
144
|
+
# entry in user profile table
|
145
|
+
global_object_square_database_helper.insert_rows_v0(
|
146
|
+
database_name=global_string_database_name,
|
147
|
+
schema_name=global_string_schema_name,
|
148
|
+
table_name=UserProfile.__tablename__,
|
149
|
+
data=[
|
150
|
+
{
|
151
|
+
UserProfile.user_id.name: local_str_user_id,
|
152
|
+
}
|
153
|
+
],
|
154
|
+
)
|
155
|
+
|
156
|
+
# entry in credential table
|
157
|
+
|
158
|
+
# hash password
|
159
|
+
local_str_hashed_password = bcrypt.hashpw(
|
160
|
+
password.encode("utf-8"), bcrypt.gensalt()
|
161
|
+
).decode("utf-8")
|
162
|
+
|
163
|
+
global_object_square_database_helper.insert_rows_v0(
|
164
|
+
data=[
|
165
|
+
{
|
166
|
+
UserCredential.user_id.name: local_str_user_id,
|
167
|
+
UserCredential.user_credential_hashed_password.name: local_str_hashed_password,
|
168
|
+
}
|
169
|
+
],
|
170
|
+
database_name=global_string_database_name,
|
171
|
+
schema_name=global_string_schema_name,
|
172
|
+
table_name=UserCredential.__tablename__,
|
173
|
+
)
|
174
|
+
if app_id is not None:
|
175
|
+
# assign app to user
|
176
|
+
global_object_square_database_helper.insert_rows_v0(
|
177
|
+
database_name=global_string_database_name,
|
178
|
+
schema_name=global_string_schema_name,
|
179
|
+
table_name=UserApp.__tablename__,
|
180
|
+
data=[
|
181
|
+
{
|
182
|
+
UserApp.user_id.name: local_str_user_id,
|
183
|
+
UserApp.app_id.name: app_id,
|
184
|
+
}
|
185
|
+
],
|
186
|
+
)
|
187
|
+
|
188
|
+
# return new access token and refresh token
|
189
|
+
# create access token
|
190
|
+
local_dict_access_token_payload = {
|
191
|
+
"app_id": app_id,
|
192
|
+
"user_id": local_str_user_id,
|
193
|
+
"exp": datetime.now(timezone.utc)
|
194
|
+
+ timedelta(minutes=config_int_access_token_valid_minutes),
|
195
|
+
}
|
196
|
+
local_str_access_token = jwt.encode(
|
197
|
+
local_dict_access_token_payload,
|
198
|
+
config_str_secret_key_for_access_token,
|
199
|
+
)
|
200
|
+
|
201
|
+
# create refresh token
|
202
|
+
local_object_refresh_token_expiry_time = datetime.now(
|
203
|
+
timezone.utc
|
204
|
+
) + timedelta(minutes=config_int_refresh_token_valid_minutes)
|
205
|
+
|
206
|
+
local_dict_refresh_token_payload = {
|
207
|
+
"app_id": app_id,
|
208
|
+
"user_id": local_str_user_id,
|
209
|
+
"exp": local_object_refresh_token_expiry_time,
|
210
|
+
}
|
211
|
+
local_str_refresh_token = jwt.encode(
|
212
|
+
local_dict_refresh_token_payload,
|
213
|
+
config_str_secret_key_for_refresh_token,
|
214
|
+
)
|
215
|
+
# entry in user session table
|
216
|
+
global_object_square_database_helper.insert_rows_v0(
|
217
|
+
data=[
|
218
|
+
{
|
219
|
+
UserSession.user_id.name: local_str_user_id,
|
220
|
+
UserSession.app_id.name: app_id,
|
221
|
+
UserSession.user_session_refresh_token.name: local_str_refresh_token,
|
222
|
+
UserSession.user_session_expiry_time.name: local_object_refresh_token_expiry_time.strftime(
|
223
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
224
|
+
),
|
225
|
+
}
|
226
|
+
],
|
227
|
+
database_name=global_string_database_name,
|
228
|
+
schema_name=global_string_schema_name,
|
229
|
+
table_name=UserSession.__tablename__,
|
230
|
+
)
|
231
|
+
"""
|
232
|
+
return value
|
233
|
+
"""
|
234
|
+
output_content = get_api_output_in_standard_format(
|
235
|
+
message=messages["REGISTRATION_SUCCESSFUL"],
|
236
|
+
data={
|
237
|
+
"main": {
|
238
|
+
"user_id": local_str_user_id,
|
239
|
+
"username": username,
|
240
|
+
"app_id": app_id,
|
241
|
+
"access_token": local_str_access_token,
|
242
|
+
"refresh_token": local_str_refresh_token,
|
243
|
+
"refresh_token_expiry_time": local_object_refresh_token_expiry_time.isoformat(),
|
244
|
+
},
|
245
|
+
},
|
246
|
+
)
|
247
|
+
return JSONResponse(
|
248
|
+
status_code=status.HTTP_201_CREATED,
|
249
|
+
content=output_content,
|
250
|
+
)
|
251
|
+
except HTTPException as http_exception:
|
252
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
253
|
+
return JSONResponse(
|
254
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
255
|
+
)
|
256
|
+
except Exception as e:
|
257
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
258
|
+
"""
|
259
|
+
rollback logic
|
260
|
+
"""
|
261
|
+
if local_str_user_id:
|
262
|
+
global_object_square_database_helper.delete_rows_v0(
|
263
|
+
database_name=global_string_database_name,
|
264
|
+
schema_name=global_string_schema_name,
|
265
|
+
table_name=User.__tablename__,
|
266
|
+
filters=FiltersV0(
|
267
|
+
root={User.user_id.name: FilterConditionsV0(eq=local_str_user_id)}
|
268
|
+
),
|
269
|
+
)
|
270
|
+
output_content = get_api_output_in_standard_format(
|
271
|
+
message=messages["GENERIC_500"],
|
272
|
+
log=str(e),
|
273
|
+
)
|
274
|
+
return JSONResponse(
|
275
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
276
|
+
)
|
277
|
+
|
278
|
+
|
279
|
+
@global_object_square_logger.auto_logger()
|
280
|
+
def util_register_login_google_v0(google_id, assign_app_id_if_missing, app_id):
|
281
|
+
was_new_user = False
|
282
|
+
try:
|
283
|
+
"""
|
284
|
+
validation
|
285
|
+
"""
|
286
|
+
# verify id token
|
287
|
+
try:
|
288
|
+
id_info = id_token.verify_oauth2_token(
|
289
|
+
google_id,
|
290
|
+
google_requests.Request(),
|
291
|
+
GOOGLE_AUTH_PLATFORM_CLIENT_ID,
|
292
|
+
)
|
293
|
+
except Exception:
|
294
|
+
output_content = get_api_output_in_standard_format(
|
295
|
+
message=messages["GENERIC_400"],
|
296
|
+
log="Google id is invalid.",
|
297
|
+
)
|
298
|
+
raise HTTPException(
|
299
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
300
|
+
detail=output_content,
|
301
|
+
)
|
302
|
+
|
303
|
+
# validate if email is verified
|
304
|
+
if id_info.get("email_verified") is not True:
|
305
|
+
output_content = get_api_output_in_standard_format(
|
306
|
+
message=messages["EMAIL_NOT_VERIFIED"],
|
307
|
+
log="Google account email is not verified.",
|
308
|
+
)
|
309
|
+
raise HTTPException(
|
310
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
311
|
+
detail=output_content,
|
312
|
+
)
|
313
|
+
"""
|
314
|
+
processing
|
315
|
+
"""
|
316
|
+
google_sub = id_info["sub"]
|
317
|
+
email = id_info.get("email")
|
318
|
+
given_name = id_info.get("given_name")
|
319
|
+
family_name = id_info.get("family_name")
|
320
|
+
|
321
|
+
profile_picture = id_info.get("picture")
|
322
|
+
|
323
|
+
# check if user exists
|
324
|
+
user_rows = global_object_square_database_helper.get_rows_v0(
|
325
|
+
database_name=global_string_database_name,
|
326
|
+
schema_name=global_string_schema_name,
|
327
|
+
table_name=UserAuthProvider.__tablename__,
|
328
|
+
filters=FiltersV0(
|
329
|
+
root={
|
330
|
+
UserAuthProvider.auth_provider_user_id.name: FilterConditionsV0(
|
331
|
+
eq=google_sub
|
332
|
+
),
|
333
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
334
|
+
eq=AuthProviderEnum.GOOGLE.value
|
335
|
+
),
|
336
|
+
}
|
337
|
+
),
|
338
|
+
)["data"]["main"]
|
339
|
+
|
340
|
+
if user_rows:
|
341
|
+
# login
|
342
|
+
|
343
|
+
# validate if app_id is assigned to user
|
344
|
+
# this will also validate if app_id is valid
|
345
|
+
local_str_user_id = user_rows[0][User.user_id.name]
|
346
|
+
user_record = global_object_square_database_helper.get_rows_v0(
|
347
|
+
database_name=global_string_database_name,
|
348
|
+
schema_name=global_string_schema_name,
|
349
|
+
table_name=User.__tablename__,
|
350
|
+
filters=FiltersV0(
|
351
|
+
root={User.user_id.name: FilterConditionsV0(eq=local_str_user_id)}
|
352
|
+
),
|
353
|
+
)["data"]["main"][0]
|
354
|
+
username = user_record[User.user_username.name]
|
355
|
+
local_list_user_app_response = (
|
356
|
+
global_object_square_database_helper.get_rows_v0(
|
357
|
+
database_name=global_string_database_name,
|
358
|
+
schema_name=global_string_schema_name,
|
359
|
+
table_name=UserApp.__tablename__,
|
360
|
+
filters=FiltersV0(
|
361
|
+
root={
|
362
|
+
UserApp.user_id.name: FilterConditionsV0(
|
363
|
+
eq=local_str_user_id
|
364
|
+
),
|
365
|
+
UserApp.app_id.name: FilterConditionsV0(eq=app_id),
|
366
|
+
}
|
367
|
+
),
|
368
|
+
)["data"]["main"]
|
369
|
+
)
|
370
|
+
if len(local_list_user_app_response) == 0:
|
371
|
+
if assign_app_id_if_missing:
|
372
|
+
global_object_square_database_helper.insert_rows_v0(
|
373
|
+
database_name=global_string_database_name,
|
374
|
+
schema_name=global_string_schema_name,
|
375
|
+
table_name=UserApp.__tablename__,
|
376
|
+
data=[
|
377
|
+
{
|
378
|
+
UserApp.user_id.name: local_str_user_id,
|
379
|
+
UserApp.app_id.name: app_id,
|
380
|
+
}
|
381
|
+
],
|
382
|
+
)
|
383
|
+
else:
|
384
|
+
output_content = get_api_output_in_standard_format(
|
385
|
+
message=messages["GENERIC_400"],
|
386
|
+
log=f"user_id {local_str_user_id}({username}) not assigned to app {app_id}.",
|
387
|
+
)
|
388
|
+
raise HTTPException(
|
389
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
390
|
+
detail=output_content,
|
391
|
+
)
|
392
|
+
else:
|
393
|
+
# register
|
394
|
+
|
395
|
+
was_new_user = True
|
396
|
+
# check if account with same email address exists
|
397
|
+
profile_rows = global_object_square_database_helper.get_rows_v0(
|
398
|
+
database_name=global_string_database_name,
|
399
|
+
schema_name=global_string_schema_name,
|
400
|
+
table_name=UserProfile.__tablename__,
|
401
|
+
filters=FiltersV0(
|
402
|
+
root={
|
403
|
+
UserProfile.user_profile_email.name: FilterConditionsV0(
|
404
|
+
eq=email
|
405
|
+
)
|
406
|
+
}
|
407
|
+
),
|
408
|
+
)["data"]["main"]
|
409
|
+
if len(profile_rows) > 0:
|
410
|
+
output_content = get_api_output_in_standard_format(
|
411
|
+
message=messages["ACCOUNT_WITH_EMAIL_ALREADY_EXISTS"],
|
412
|
+
log=f"An account with the email {email} already exists.",
|
413
|
+
)
|
414
|
+
raise HTTPException(
|
415
|
+
status_code=status.HTTP_409_CONFLICT,
|
416
|
+
detail=output_content,
|
417
|
+
)
|
418
|
+
# generate a default username
|
419
|
+
username = generate_default_username_for_google_users(
|
420
|
+
family_name=family_name, given_name=given_name
|
421
|
+
)
|
422
|
+
# create user
|
423
|
+
user_rows = global_object_square_database_helper.insert_rows_v0(
|
424
|
+
database_name=global_string_database_name,
|
425
|
+
schema_name=global_string_schema_name,
|
426
|
+
table_name=User.__tablename__,
|
427
|
+
data=[
|
428
|
+
{
|
429
|
+
User.user_username.name: username,
|
430
|
+
}
|
431
|
+
],
|
432
|
+
)["data"]["main"]
|
433
|
+
local_str_user_id = user_rows[0][User.user_id.name]
|
434
|
+
|
435
|
+
# link to user_auth_provider
|
436
|
+
global_object_square_database_helper.insert_rows_v0(
|
437
|
+
database_name=global_string_database_name,
|
438
|
+
schema_name=global_string_schema_name,
|
439
|
+
table_name=UserAuthProvider.__tablename__,
|
440
|
+
data=[
|
441
|
+
{
|
442
|
+
UserAuthProvider.user_id.name: local_str_user_id,
|
443
|
+
UserAuthProvider.auth_provider.name: AuthProviderEnum.GOOGLE.value,
|
444
|
+
UserAuthProvider.auth_provider_user_id.name: google_sub,
|
445
|
+
}
|
446
|
+
],
|
447
|
+
)
|
448
|
+
# getting profile picture
|
449
|
+
if profile_picture:
|
450
|
+
try:
|
451
|
+
profile_picture_response = make_request(
|
452
|
+
"GET", profile_picture, return_type="response"
|
453
|
+
)
|
454
|
+
|
455
|
+
# finding content type and filename
|
456
|
+
headers = profile_picture_response.headers
|
457
|
+
content_type = headers.get(
|
458
|
+
"Content-Type", "application/octet-stream"
|
459
|
+
)
|
460
|
+
content_disposition = headers.get("Content-Disposition", "")
|
461
|
+
|
462
|
+
if content_disposition:
|
463
|
+
match = re.search(r'filename="([^"]+)"', content_disposition)
|
464
|
+
if match:
|
465
|
+
filename = match.group(1)
|
466
|
+
else:
|
467
|
+
filename = None
|
468
|
+
else:
|
469
|
+
filename = None
|
470
|
+
if filename is None:
|
471
|
+
global_object_square_logger.logger.warning(
|
472
|
+
f"user_id {local_str_user_id}'s profile picture from Google missing filename; guessing extension from Content-Type: {content_type}."
|
473
|
+
)
|
474
|
+
ext = (
|
475
|
+
mimetypes.guess_extension(
|
476
|
+
content_type.split(";")[0].strip()
|
477
|
+
)
|
478
|
+
or ""
|
479
|
+
)
|
480
|
+
filename = f"profile_photo{ext}"
|
481
|
+
if not ext:
|
482
|
+
filename += ".bin"
|
483
|
+
|
484
|
+
# upload bytes to square_file_storage
|
485
|
+
file_upload_response = global_object_square_file_store_helper.upload_file_using_tuple_v0(
|
486
|
+
file=(
|
487
|
+
filename,
|
488
|
+
io.BytesIO(profile_picture_response.content),
|
489
|
+
content_type,
|
490
|
+
),
|
491
|
+
system_relative_path="global/users/profile_photos",
|
492
|
+
)
|
493
|
+
user_profile_photo_storage_token = file_upload_response["data"][
|
494
|
+
"main"
|
495
|
+
]
|
496
|
+
except HTTPError:
|
497
|
+
global_object_square_logger.logger.error(
|
498
|
+
f"Failed to fetch profile picture for user_id {local_str_user_id} from google account.",
|
499
|
+
exc_info=True,
|
500
|
+
)
|
501
|
+
user_profile_photo_storage_token = None
|
502
|
+
except Exception as e:
|
503
|
+
global_object_square_logger.logger.error(
|
504
|
+
f"Error while fetching profile picture for user_id {local_str_user_id} from google account: {str(e)}",
|
505
|
+
exc_info=True,
|
506
|
+
)
|
507
|
+
user_profile_photo_storage_token = None
|
508
|
+
else:
|
509
|
+
global_object_square_logger.logger.warning(
|
510
|
+
f"user_id {local_str_user_id} has no profile picture in google account."
|
511
|
+
)
|
512
|
+
user_profile_photo_storage_token = None
|
513
|
+
# create user profile
|
514
|
+
global_object_square_database_helper.insert_rows_v0(
|
515
|
+
database_name=global_string_database_name,
|
516
|
+
schema_name=global_string_schema_name,
|
517
|
+
table_name=UserProfile.__tablename__,
|
518
|
+
data=[
|
519
|
+
{
|
520
|
+
UserProfile.user_id.name: local_str_user_id,
|
521
|
+
UserProfile.user_profile_email.name: email,
|
522
|
+
UserProfile.user_profile_email_verified.name: datetime.now(
|
523
|
+
timezone.utc
|
524
|
+
).strftime("%Y-%m-%d %H:%M:%S.%f+00"),
|
525
|
+
UserProfile.user_profile_first_name.name: given_name,
|
526
|
+
UserProfile.user_profile_last_name.name: family_name,
|
527
|
+
UserProfile.user_profile_photo_storage_token.name: user_profile_photo_storage_token,
|
528
|
+
}
|
529
|
+
],
|
530
|
+
)
|
531
|
+
|
532
|
+
# assign app if provided
|
533
|
+
if app_id is not None:
|
534
|
+
global_object_square_database_helper.insert_rows_v0(
|
535
|
+
database_name=global_string_database_name,
|
536
|
+
schema_name=global_string_schema_name,
|
537
|
+
table_name=UserApp.__tablename__,
|
538
|
+
data=[
|
539
|
+
{
|
540
|
+
UserApp.user_id.name: local_str_user_id,
|
541
|
+
UserApp.app_id.name: app_id,
|
542
|
+
}
|
543
|
+
],
|
544
|
+
)
|
545
|
+
|
546
|
+
# generate tokens
|
547
|
+
now = datetime.now(timezone.utc)
|
548
|
+
access_token_payload = {
|
549
|
+
"app_id": app_id,
|
550
|
+
"user_id": local_str_user_id,
|
551
|
+
"exp": now + timedelta(minutes=config_int_access_token_valid_minutes),
|
552
|
+
}
|
553
|
+
access_token_str = jwt.encode(
|
554
|
+
access_token_payload,
|
555
|
+
config_str_secret_key_for_access_token,
|
556
|
+
)
|
557
|
+
|
558
|
+
refresh_token_expiry = now + timedelta(
|
559
|
+
minutes=config_int_refresh_token_valid_minutes
|
560
|
+
)
|
561
|
+
refresh_token_payload = {
|
562
|
+
"app_id": app_id,
|
563
|
+
"user_id": local_str_user_id,
|
564
|
+
"exp": refresh_token_expiry,
|
565
|
+
}
|
566
|
+
refresh_token_str = jwt.encode(
|
567
|
+
refresh_token_payload,
|
568
|
+
config_str_secret_key_for_refresh_token,
|
569
|
+
)
|
570
|
+
|
571
|
+
# store refresh token
|
572
|
+
global_object_square_database_helper.insert_rows_v0(
|
573
|
+
database_name=global_string_database_name,
|
574
|
+
schema_name=global_string_schema_name,
|
575
|
+
table_name=UserSession.__tablename__,
|
576
|
+
data=[
|
577
|
+
{
|
578
|
+
UserSession.user_id.name: local_str_user_id,
|
579
|
+
UserSession.app_id.name: app_id,
|
580
|
+
UserSession.user_session_refresh_token.name: refresh_token_str,
|
581
|
+
UserSession.user_session_expiry_time.name: refresh_token_expiry.strftime(
|
582
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
583
|
+
),
|
584
|
+
}
|
585
|
+
],
|
586
|
+
)
|
587
|
+
"""
|
588
|
+
return value
|
589
|
+
"""
|
590
|
+
if was_new_user:
|
591
|
+
message = messages["REGISTRATION_SUCCESSFUL"]
|
592
|
+
else:
|
593
|
+
message = messages["LOGIN_SUCCESSFUL"]
|
594
|
+
output_content = get_api_output_in_standard_format(
|
595
|
+
message=message,
|
596
|
+
data={
|
597
|
+
"main": {
|
598
|
+
"user_id": local_str_user_id,
|
599
|
+
"username": username,
|
600
|
+
"app_id": app_id,
|
601
|
+
"access_token": access_token_str,
|
602
|
+
"refresh_token": refresh_token_str,
|
603
|
+
"refresh_token_expiry_time": refresh_token_expiry.isoformat(),
|
604
|
+
"was_new_user": was_new_user,
|
605
|
+
},
|
606
|
+
},
|
607
|
+
)
|
608
|
+
|
609
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
610
|
+
except HTTPError as http_error:
|
611
|
+
global_object_square_logger.logger.error(http_error, exc_info=True)
|
612
|
+
return JSONResponse(
|
613
|
+
status_code=http_error.response.status_code,
|
614
|
+
content=http_error.response.text,
|
615
|
+
)
|
616
|
+
except HTTPException as http_exception:
|
617
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
618
|
+
return JSONResponse(
|
619
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
620
|
+
)
|
621
|
+
except Exception as e:
|
622
|
+
"""
|
623
|
+
rollback logic
|
624
|
+
"""
|
625
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
626
|
+
output_content = get_api_output_in_standard_format(
|
627
|
+
message=messages["GENERIC_500"],
|
628
|
+
log=str(e),
|
629
|
+
)
|
630
|
+
return JSONResponse(
|
631
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
632
|
+
content=output_content,
|
633
|
+
)
|
634
|
+
|
635
|
+
|
636
|
+
@global_object_square_logger.auto_logger()
|
637
|
+
def util_get_user_details_v0(access_token):
|
638
|
+
try:
|
639
|
+
"""
|
640
|
+
validation
|
641
|
+
"""
|
642
|
+
# validate access token
|
643
|
+
try:
|
644
|
+
local_dict_access_token_payload = get_jwt_payload(
|
645
|
+
access_token, config_str_secret_key_for_access_token
|
646
|
+
)
|
647
|
+
except Exception as error:
|
648
|
+
output_content = get_api_output_in_standard_format(
|
649
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
650
|
+
)
|
651
|
+
raise HTTPException(
|
652
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
653
|
+
detail=output_content,
|
654
|
+
)
|
655
|
+
user_id = local_dict_access_token_payload["user_id"]
|
656
|
+
"""
|
657
|
+
main process
|
658
|
+
"""
|
659
|
+
local_list_user = global_object_square_database_helper.get_rows_v0(
|
660
|
+
database_name=global_string_database_name,
|
661
|
+
schema_name=global_string_schema_name,
|
662
|
+
table_name=User.__tablename__,
|
663
|
+
filters=FiltersV0(
|
664
|
+
root={
|
665
|
+
User.user_id.name: FilterConditionsV0(eq=user_id),
|
666
|
+
}
|
667
|
+
),
|
668
|
+
)["data"]["main"]
|
669
|
+
local_list_app = global_object_square_database_helper.get_rows_v0(
|
670
|
+
database_name=global_string_database_name,
|
671
|
+
schema_name=global_string_public_schema_name,
|
672
|
+
table_name=App.__tablename__,
|
673
|
+
apply_filters=False,
|
674
|
+
filters=FiltersV0(root={}),
|
675
|
+
)["data"]["main"]
|
676
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
677
|
+
database_name=global_string_database_name,
|
678
|
+
schema_name=global_string_schema_name,
|
679
|
+
table_name=UserApp.__tablename__,
|
680
|
+
filters=FiltersV0(
|
681
|
+
root={UserApp.user_id.name: FilterConditionsV0(eq=user_id)}
|
682
|
+
),
|
683
|
+
)["data"]["main"]
|
684
|
+
local_list_response_user_profile = (
|
685
|
+
global_object_square_database_helper.get_rows_v0(
|
686
|
+
database_name=global_string_database_name,
|
687
|
+
schema_name=global_string_schema_name,
|
688
|
+
table_name=UserProfile.__tablename__,
|
689
|
+
filters=FiltersV0(
|
690
|
+
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
691
|
+
),
|
692
|
+
)["data"]["main"]
|
693
|
+
)
|
694
|
+
local_list_response_user_sessions = (
|
695
|
+
global_object_square_database_helper.get_rows_v0(
|
696
|
+
database_name=global_string_database_name,
|
697
|
+
schema_name=global_string_schema_name,
|
698
|
+
table_name=UserSession.__tablename__,
|
699
|
+
filters=FiltersV0(
|
700
|
+
root={
|
701
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
702
|
+
UserSession.user_session_expiry_time.name: FilterConditionsV0(
|
703
|
+
gte=datetime.now(timezone.utc).isoformat()
|
704
|
+
),
|
705
|
+
}
|
706
|
+
),
|
707
|
+
)["data"]["main"]
|
708
|
+
)
|
709
|
+
user_profile = copy.deepcopy(local_list_response_user_profile[0])
|
710
|
+
del user_profile[UserProfile.user_id.name]
|
711
|
+
"""
|
712
|
+
return value
|
713
|
+
"""
|
714
|
+
return_this = {
|
715
|
+
"user_id": user_id,
|
716
|
+
"username": local_list_user[0][User.user_username.name],
|
717
|
+
"profile": user_profile,
|
718
|
+
"apps": [
|
719
|
+
y[App.app_name.name]
|
720
|
+
for y in local_list_app
|
721
|
+
if y[App.app_id.name]
|
722
|
+
in [x[UserApp.app_id.name] for x in local_list_response_user_app]
|
723
|
+
],
|
724
|
+
"sessions": [
|
725
|
+
{
|
726
|
+
"app_name": [
|
727
|
+
y[App.app_name.name]
|
728
|
+
for y in local_list_app
|
729
|
+
if y[App.app_id.name] == x[UserApp.app_id.name]
|
730
|
+
][0],
|
731
|
+
"active_sessions": len(
|
732
|
+
[
|
733
|
+
y
|
734
|
+
for y in local_list_response_user_sessions
|
735
|
+
if y[UserSession.app_id.name] == x[UserApp.app_id.name]
|
736
|
+
]
|
737
|
+
),
|
738
|
+
}
|
739
|
+
for x in local_list_response_user_app
|
740
|
+
],
|
741
|
+
}
|
742
|
+
output_content = get_api_output_in_standard_format(
|
743
|
+
message=messages["GENERIC_READ_SUCCESSFUL"],
|
744
|
+
data={"main": return_this},
|
745
|
+
)
|
746
|
+
return JSONResponse(
|
747
|
+
status_code=status.HTTP_200_OK,
|
748
|
+
content=output_content,
|
749
|
+
)
|
750
|
+
except HTTPException as http_exception:
|
751
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
752
|
+
return JSONResponse(
|
753
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
754
|
+
)
|
755
|
+
except Exception as e:
|
756
|
+
"""
|
757
|
+
rollback logic
|
758
|
+
"""
|
759
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
760
|
+
output_content = get_api_output_in_standard_format(
|
761
|
+
message=messages["GENERIC_500"],
|
762
|
+
log=str(e),
|
763
|
+
)
|
764
|
+
return JSONResponse(
|
765
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
766
|
+
content=output_content,
|
767
|
+
)
|
768
|
+
|
769
|
+
|
770
|
+
@global_object_square_logger.auto_logger()
|
771
|
+
def util_update_user_app_ids_v0(access_token, app_ids_to_add, app_ids_to_remove):
|
772
|
+
try:
|
773
|
+
|
774
|
+
"""
|
775
|
+
validation
|
776
|
+
"""
|
777
|
+
# validate access token
|
778
|
+
try:
|
779
|
+
local_dict_access_token_payload = get_jwt_payload(
|
780
|
+
access_token, config_str_secret_key_for_access_token
|
781
|
+
)
|
782
|
+
except Exception as error:
|
783
|
+
output_content = get_api_output_in_standard_format(
|
784
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
785
|
+
)
|
786
|
+
raise HTTPException(
|
787
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
788
|
+
detail=output_content,
|
789
|
+
)
|
790
|
+
user_id = local_dict_access_token_payload["user_id"]
|
791
|
+
|
792
|
+
app_ids_to_add = list(set(app_ids_to_add))
|
793
|
+
app_ids_to_remove = list(set(app_ids_to_remove))
|
794
|
+
|
795
|
+
# check if app_ids_to_add and app_ids_to_remove don't have common ids.
|
796
|
+
local_list_common_app_ids = set(app_ids_to_add) & set(app_ids_to_remove)
|
797
|
+
if len(local_list_common_app_ids) > 0:
|
798
|
+
output_content = get_api_output_in_standard_format(
|
799
|
+
message=messages["GENERIC_400"],
|
800
|
+
log=f"invalid app_ids: {list(local_list_common_app_ids)}, present in both add list and remove list.",
|
801
|
+
)
|
802
|
+
raise HTTPException(
|
803
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
804
|
+
detail=output_content,
|
805
|
+
)
|
806
|
+
|
807
|
+
# check if all app_ids are valid
|
808
|
+
local_list_all_app_ids = [*app_ids_to_add, *app_ids_to_remove]
|
809
|
+
local_list_response_app = global_object_square_database_helper.get_rows_v0(
|
810
|
+
database_name=global_string_database_name,
|
811
|
+
schema_name=global_string_public_schema_name,
|
812
|
+
table_name=App.__tablename__,
|
813
|
+
apply_filters=False,
|
814
|
+
filters=FiltersV0(root={}),
|
815
|
+
)["data"]["main"]
|
816
|
+
local_list_invalid_ids = [
|
817
|
+
x
|
818
|
+
for x in local_list_all_app_ids
|
819
|
+
if x not in [y[App.app_id.name] for y in local_list_response_app]
|
820
|
+
]
|
821
|
+
if len(local_list_invalid_ids) > 0:
|
822
|
+
output_content = get_api_output_in_standard_format(
|
823
|
+
message=messages["GENERIC_400"],
|
824
|
+
log=f"invalid app_ids: {local_list_invalid_ids}.",
|
825
|
+
)
|
826
|
+
raise HTTPException(
|
827
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
828
|
+
detail=output_content,
|
829
|
+
)
|
830
|
+
"""
|
831
|
+
main process
|
832
|
+
"""
|
833
|
+
# logic for adding new app_ids
|
834
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
835
|
+
database_name=global_string_database_name,
|
836
|
+
schema_name=global_string_schema_name,
|
837
|
+
table_name=UserApp.__tablename__,
|
838
|
+
filters=FiltersV0(
|
839
|
+
root={UserApp.user_id.name: FilterConditionsV0(eq=user_id)}
|
840
|
+
),
|
841
|
+
)["data"]["main"]
|
842
|
+
local_list_new_app_ids = [
|
843
|
+
{
|
844
|
+
UserApp.user_id.name: user_id,
|
845
|
+
UserApp.app_id.name: x,
|
846
|
+
}
|
847
|
+
for x in app_ids_to_add
|
848
|
+
if x not in [y[UserApp.app_id.name] for y in local_list_response_user_app]
|
849
|
+
]
|
850
|
+
if len(local_list_new_app_ids) > 0:
|
851
|
+
global_object_square_database_helper.insert_rows_v0(
|
852
|
+
database_name=global_string_database_name,
|
853
|
+
schema_name=global_string_schema_name,
|
854
|
+
table_name=UserApp.__tablename__,
|
855
|
+
data=local_list_new_app_ids,
|
856
|
+
)
|
857
|
+
|
858
|
+
# logic for removing app_ids
|
859
|
+
for app_id in app_ids_to_remove:
|
860
|
+
global_object_square_database_helper.delete_rows_v0(
|
861
|
+
database_name=global_string_database_name,
|
862
|
+
schema_name=global_string_schema_name,
|
863
|
+
table_name=UserApp.__tablename__,
|
864
|
+
filters=FiltersV0(
|
865
|
+
root={
|
866
|
+
UserApp.user_id.name: FilterConditionsV0(eq=user_id),
|
867
|
+
UserApp.app_id.name: FilterConditionsV0(eq=app_id),
|
868
|
+
}
|
869
|
+
),
|
870
|
+
)
|
871
|
+
# logout user from removed apps
|
872
|
+
global_object_square_database_helper.delete_rows_v0(
|
873
|
+
database_name=global_string_database_name,
|
874
|
+
schema_name=global_string_schema_name,
|
875
|
+
table_name=UserSession.__tablename__,
|
876
|
+
filters=FiltersV0(
|
877
|
+
root={
|
878
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
879
|
+
UserSession.app_id.name: FilterConditionsV0(eq=app_id),
|
880
|
+
}
|
881
|
+
),
|
882
|
+
)
|
883
|
+
|
884
|
+
"""
|
885
|
+
return value
|
886
|
+
"""
|
887
|
+
# get latest app ids
|
888
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
889
|
+
database_name=global_string_database_name,
|
890
|
+
schema_name=global_string_schema_name,
|
891
|
+
table_name=UserApp.__tablename__,
|
892
|
+
filters=FiltersV0(
|
893
|
+
root={UserApp.user_id.name: FilterConditionsV0(eq=user_id)}
|
894
|
+
),
|
895
|
+
)["data"]["main"]
|
896
|
+
output_content = get_api_output_in_standard_format(
|
897
|
+
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
898
|
+
data={
|
899
|
+
"main": [x[UserApp.app_id.name] for x in local_list_response_user_app]
|
900
|
+
},
|
901
|
+
)
|
902
|
+
return JSONResponse(
|
903
|
+
status_code=status.HTTP_200_OK,
|
904
|
+
content=output_content,
|
905
|
+
)
|
906
|
+
except HTTPException as http_exception:
|
907
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
908
|
+
return JSONResponse(
|
909
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
910
|
+
)
|
911
|
+
except Exception as e:
|
912
|
+
"""
|
913
|
+
rollback logic
|
914
|
+
"""
|
915
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
916
|
+
output_content = get_api_output_in_standard_format(
|
917
|
+
message=messages["GENERIC_500"],
|
918
|
+
log=str(e),
|
919
|
+
)
|
920
|
+
return JSONResponse(
|
921
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
922
|
+
content=output_content,
|
923
|
+
)
|
924
|
+
|
925
|
+
|
926
|
+
@global_object_square_logger.auto_logger()
|
927
|
+
def util_login_username_v0(username, password, app_id, assign_app_id_if_missing):
|
928
|
+
try:
|
929
|
+
"""
|
930
|
+
validation
|
931
|
+
"""
|
932
|
+
# validation for username
|
933
|
+
# check if user with username exists
|
934
|
+
local_list_response_user = global_object_square_database_helper.get_rows_v0(
|
935
|
+
database_name=global_string_database_name,
|
936
|
+
schema_name=global_string_schema_name,
|
937
|
+
table_name=User.__tablename__,
|
938
|
+
filters=FiltersV0(
|
939
|
+
root={User.user_username.name: FilterConditionsV0(eq=username)}
|
940
|
+
),
|
941
|
+
)["data"]["main"]
|
942
|
+
if len(local_list_response_user) != 1:
|
943
|
+
output_content = get_api_output_in_standard_format(
|
944
|
+
message=messages["INCORRECT_USERNAME"],
|
945
|
+
log=f"incorrect username {username}",
|
946
|
+
)
|
947
|
+
raise HTTPException(
|
948
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
949
|
+
detail=output_content,
|
950
|
+
)
|
951
|
+
# check if user has auth provider as SELF
|
952
|
+
local_list_user_auth_provider_response = (
|
953
|
+
global_object_square_database_helper.get_rows_v0(
|
954
|
+
database_name=global_string_database_name,
|
955
|
+
schema_name=global_string_schema_name,
|
956
|
+
table_name=UserAuthProvider.__tablename__,
|
957
|
+
filters=FiltersV0(
|
958
|
+
root={
|
959
|
+
UserAuthProvider.user_id.name: FilterConditionsV0(
|
960
|
+
eq=local_list_response_user[0][User.user_id.name]
|
961
|
+
),
|
962
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
963
|
+
eq=AuthProviderEnum.SELF.value
|
964
|
+
),
|
965
|
+
}
|
966
|
+
),
|
967
|
+
)["data"]["main"]
|
968
|
+
)
|
969
|
+
if len(local_list_user_auth_provider_response) != 1:
|
970
|
+
output_content = get_api_output_in_standard_format(
|
971
|
+
message=messages["INCORRECT_AUTH_PROVIDER"],
|
972
|
+
log=f"{username} not linked with {AuthProviderEnum.SELF.value} auth provider.",
|
973
|
+
)
|
974
|
+
raise HTTPException(
|
975
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
976
|
+
detail=output_content,
|
977
|
+
)
|
978
|
+
# check if user has credentials (might not be set in case of errors in registration.)
|
979
|
+
local_list_authentication_user_response = (
|
980
|
+
global_object_square_database_helper.get_rows_v0(
|
981
|
+
database_name=global_string_database_name,
|
982
|
+
schema_name=global_string_schema_name,
|
983
|
+
table_name=UserCredential.__tablename__,
|
984
|
+
filters=FiltersV0(
|
985
|
+
root={
|
986
|
+
UserCredential.user_id.name: FilterConditionsV0(
|
987
|
+
eq=local_list_response_user[0][User.user_id.name]
|
988
|
+
)
|
989
|
+
}
|
990
|
+
),
|
991
|
+
)["data"]["main"]
|
992
|
+
)
|
993
|
+
if len(local_list_authentication_user_response) != 1:
|
994
|
+
output_content = get_api_output_in_standard_format(
|
995
|
+
message=messages["MALFORMED_USER"],
|
996
|
+
log=f"username: {username} does not have credentials set.",
|
997
|
+
)
|
998
|
+
raise HTTPException(
|
999
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1000
|
+
detail=output_content,
|
1001
|
+
)
|
1002
|
+
# validate if app_id is assigned to user
|
1003
|
+
# this will also validate if app_id is valid
|
1004
|
+
local_dict_user = local_list_authentication_user_response[0]
|
1005
|
+
local_str_user_id = local_dict_user[UserCredential.user_id.name]
|
1006
|
+
local_list_user_app_response = global_object_square_database_helper.get_rows_v0(
|
1007
|
+
database_name=global_string_database_name,
|
1008
|
+
schema_name=global_string_schema_name,
|
1009
|
+
table_name=UserApp.__tablename__,
|
1010
|
+
filters=FiltersV0(
|
1011
|
+
root={
|
1012
|
+
UserApp.user_id.name: FilterConditionsV0(eq=local_str_user_id),
|
1013
|
+
UserApp.app_id.name: FilterConditionsV0(eq=app_id),
|
1014
|
+
}
|
1015
|
+
),
|
1016
|
+
)["data"]["main"]
|
1017
|
+
if len(local_list_user_app_response) == 0:
|
1018
|
+
if assign_app_id_if_missing:
|
1019
|
+
try:
|
1020
|
+
global_object_square_database_helper.insert_rows_v0(
|
1021
|
+
database_name=global_string_database_name,
|
1022
|
+
schema_name=global_string_schema_name,
|
1023
|
+
table_name=UserApp.__tablename__,
|
1024
|
+
data=[
|
1025
|
+
{
|
1026
|
+
UserApp.user_id.name: local_str_user_id,
|
1027
|
+
UserApp.app_id.name: app_id,
|
1028
|
+
}
|
1029
|
+
],
|
1030
|
+
)
|
1031
|
+
except HTTPError as he:
|
1032
|
+
output_content = get_api_output_in_standard_format(
|
1033
|
+
message=messages["GENERIC_400"],
|
1034
|
+
log=str(he),
|
1035
|
+
)
|
1036
|
+
raise HTTPException(
|
1037
|
+
status_code=he.response.status_code, detail=output_content
|
1038
|
+
)
|
1039
|
+
else:
|
1040
|
+
output_content = get_api_output_in_standard_format(
|
1041
|
+
message=messages["GENERIC_400"],
|
1042
|
+
log=f"user_id {local_str_user_id}({username}) not assigned to app {app_id}.",
|
1043
|
+
)
|
1044
|
+
raise HTTPException(
|
1045
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1046
|
+
detail=output_content,
|
1047
|
+
)
|
1048
|
+
|
1049
|
+
# validate password
|
1050
|
+
if not (
|
1051
|
+
bcrypt.checkpw(
|
1052
|
+
password.encode("utf-8"),
|
1053
|
+
local_dict_user[
|
1054
|
+
UserCredential.user_credential_hashed_password.name
|
1055
|
+
].encode("utf-8"),
|
1056
|
+
)
|
1057
|
+
):
|
1058
|
+
output_content = get_api_output_in_standard_format(
|
1059
|
+
message=messages["INCORRECT_PASSWORD"],
|
1060
|
+
log=f"incorrect password for user_id {local_str_user_id}({username}).",
|
1061
|
+
)
|
1062
|
+
raise HTTPException(
|
1063
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1064
|
+
detail=output_content,
|
1065
|
+
)
|
1066
|
+
"""
|
1067
|
+
main process
|
1068
|
+
"""
|
1069
|
+
# return new access token and refresh token
|
1070
|
+
|
1071
|
+
# create access token
|
1072
|
+
local_dict_access_token_payload = {
|
1073
|
+
"app_id": app_id,
|
1074
|
+
"user_id": local_str_user_id,
|
1075
|
+
"exp": datetime.now(timezone.utc)
|
1076
|
+
+ timedelta(minutes=config_int_access_token_valid_minutes),
|
1077
|
+
}
|
1078
|
+
local_str_access_token = jwt.encode(
|
1079
|
+
local_dict_access_token_payload,
|
1080
|
+
config_str_secret_key_for_access_token,
|
1081
|
+
)
|
1082
|
+
|
1083
|
+
# create refresh token
|
1084
|
+
local_object_refresh_token_expiry_time = datetime.now(timezone.utc) + timedelta(
|
1085
|
+
minutes=config_int_refresh_token_valid_minutes
|
1086
|
+
)
|
1087
|
+
|
1088
|
+
local_dict_refresh_token_payload = {
|
1089
|
+
"app_id": app_id,
|
1090
|
+
"user_id": local_str_user_id,
|
1091
|
+
"exp": local_object_refresh_token_expiry_time,
|
1092
|
+
}
|
1093
|
+
local_str_refresh_token = jwt.encode(
|
1094
|
+
local_dict_refresh_token_payload,
|
1095
|
+
config_str_secret_key_for_refresh_token,
|
1096
|
+
)
|
1097
|
+
# entry in user session table
|
1098
|
+
global_object_square_database_helper.insert_rows_v0(
|
1099
|
+
data=[
|
1100
|
+
{
|
1101
|
+
UserSession.user_id.name: local_str_user_id,
|
1102
|
+
UserSession.app_id.name: app_id,
|
1103
|
+
UserSession.user_session_refresh_token.name: local_str_refresh_token,
|
1104
|
+
UserSession.user_session_expiry_time.name: local_object_refresh_token_expiry_time.strftime(
|
1105
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
1106
|
+
),
|
1107
|
+
}
|
1108
|
+
],
|
1109
|
+
database_name=global_string_database_name,
|
1110
|
+
schema_name=global_string_schema_name,
|
1111
|
+
table_name=UserSession.__tablename__,
|
1112
|
+
)
|
1113
|
+
"""
|
1114
|
+
return value
|
1115
|
+
"""
|
1116
|
+
output_content = get_api_output_in_standard_format(
|
1117
|
+
data={
|
1118
|
+
"main": {
|
1119
|
+
"user_id": local_str_user_id,
|
1120
|
+
"access_token": local_str_access_token,
|
1121
|
+
"refresh_token": local_str_refresh_token,
|
1122
|
+
"refresh_token_expiry_time": local_object_refresh_token_expiry_time.isoformat(),
|
1123
|
+
}
|
1124
|
+
},
|
1125
|
+
message=messages["LOGIN_SUCCESSFUL"],
|
1126
|
+
)
|
1127
|
+
return JSONResponse(
|
1128
|
+
status_code=status.HTTP_200_OK,
|
1129
|
+
content=output_content,
|
1130
|
+
)
|
1131
|
+
except HTTPException as http_exception:
|
1132
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1133
|
+
return JSONResponse(
|
1134
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1135
|
+
)
|
1136
|
+
except Exception as e:
|
1137
|
+
"""
|
1138
|
+
rollback logic
|
1139
|
+
"""
|
1140
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1141
|
+
output_content = get_api_output_in_standard_format(
|
1142
|
+
message=messages["GENERIC_500"],
|
1143
|
+
log=str(e),
|
1144
|
+
)
|
1145
|
+
return JSONResponse(
|
1146
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1147
|
+
)
|
1148
|
+
|
1149
|
+
|
1150
|
+
@global_object_square_logger.auto_logger()
|
1151
|
+
def util_generate_access_token_v0(refresh_token):
|
1152
|
+
try:
|
1153
|
+
"""
|
1154
|
+
validation
|
1155
|
+
"""
|
1156
|
+
# validate refresh token
|
1157
|
+
# validating if a session refresh token exists in the database.
|
1158
|
+
local_list_user_session_response = (
|
1159
|
+
global_object_square_database_helper.get_rows_v0(
|
1160
|
+
database_name=global_string_database_name,
|
1161
|
+
schema_name=global_string_schema_name,
|
1162
|
+
table_name=UserSession.__tablename__,
|
1163
|
+
filters=FiltersV0(
|
1164
|
+
root={
|
1165
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1166
|
+
eq=refresh_token
|
1167
|
+
),
|
1168
|
+
}
|
1169
|
+
),
|
1170
|
+
)["data"]["main"]
|
1171
|
+
)
|
1172
|
+
|
1173
|
+
if len(local_list_user_session_response) != 1:
|
1174
|
+
output_content = get_api_output_in_standard_format(
|
1175
|
+
message=messages["INCORRECT_REFRESH_TOKEN"],
|
1176
|
+
log=f"incorrect refresh token: {refresh_token}.",
|
1177
|
+
)
|
1178
|
+
raise HTTPException(
|
1179
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1180
|
+
detail=output_content,
|
1181
|
+
)
|
1182
|
+
# validating if the refresh token is valid, active and of the same user.
|
1183
|
+
try:
|
1184
|
+
local_dict_refresh_token_payload = get_jwt_payload(
|
1185
|
+
refresh_token, config_str_secret_key_for_refresh_token
|
1186
|
+
)
|
1187
|
+
except Exception as error:
|
1188
|
+
output_content = get_api_output_in_standard_format(
|
1189
|
+
message=messages["INCORRECT_REFRESH_TOKEN"], log=str(error)
|
1190
|
+
)
|
1191
|
+
raise HTTPException(
|
1192
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1193
|
+
detail=output_content,
|
1194
|
+
)
|
1195
|
+
"""
|
1196
|
+
main process
|
1197
|
+
"""
|
1198
|
+
# create and send access token
|
1199
|
+
local_dict_access_token_payload = {
|
1200
|
+
"app_id": local_dict_refresh_token_payload["app_id"],
|
1201
|
+
"user_id": local_dict_refresh_token_payload["user_id"],
|
1202
|
+
"exp": datetime.now(timezone.utc)
|
1203
|
+
+ timedelta(minutes=config_int_access_token_valid_minutes),
|
1204
|
+
}
|
1205
|
+
local_str_access_token = jwt.encode(
|
1206
|
+
local_dict_access_token_payload, config_str_secret_key_for_access_token
|
1207
|
+
)
|
1208
|
+
"""
|
1209
|
+
return value
|
1210
|
+
"""
|
1211
|
+
output_content = get_api_output_in_standard_format(
|
1212
|
+
data={"main": {"access_token": local_str_access_token}},
|
1213
|
+
message=messages["GENERIC_CREATION_SUCCESSFUL"],
|
1214
|
+
)
|
1215
|
+
return JSONResponse(
|
1216
|
+
status_code=status.HTTP_200_OK,
|
1217
|
+
content=output_content,
|
1218
|
+
)
|
1219
|
+
except HTTPException as http_exception:
|
1220
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1221
|
+
return JSONResponse(
|
1222
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1223
|
+
)
|
1224
|
+
except Exception as e:
|
1225
|
+
"""
|
1226
|
+
rollback logic
|
1227
|
+
"""
|
1228
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1229
|
+
output_content = get_api_output_in_standard_format(
|
1230
|
+
message=messages["GENERIC_500"],
|
1231
|
+
log=str(e),
|
1232
|
+
)
|
1233
|
+
return JSONResponse(
|
1234
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1235
|
+
)
|
1236
|
+
|
1237
|
+
|
1238
|
+
@global_object_square_logger.auto_logger()
|
1239
|
+
def util_logout_v0(refresh_token):
|
1240
|
+
try:
|
1241
|
+
"""
|
1242
|
+
validation
|
1243
|
+
"""
|
1244
|
+
# validate refresh token
|
1245
|
+
# validating if a session refresh token exists in the database.
|
1246
|
+
local_list_user_session_response = (
|
1247
|
+
global_object_square_database_helper.get_rows_v0(
|
1248
|
+
database_name=global_string_database_name,
|
1249
|
+
schema_name=global_string_schema_name,
|
1250
|
+
table_name=UserSession.__tablename__,
|
1251
|
+
filters=FiltersV0(
|
1252
|
+
root={
|
1253
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1254
|
+
eq=refresh_token
|
1255
|
+
),
|
1256
|
+
}
|
1257
|
+
),
|
1258
|
+
)["data"]["main"]
|
1259
|
+
)
|
1260
|
+
|
1261
|
+
if len(local_list_user_session_response) != 1:
|
1262
|
+
output_content = get_api_output_in_standard_format(
|
1263
|
+
message=messages["INCORRECT_REFRESH_TOKEN"],
|
1264
|
+
log=f"incorrect refresh token: {refresh_token}.",
|
1265
|
+
)
|
1266
|
+
raise HTTPException(
|
1267
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1268
|
+
detail=output_content,
|
1269
|
+
)
|
1270
|
+
# validating if the refresh token is valid, active and of the same user.
|
1271
|
+
try:
|
1272
|
+
_ = get_jwt_payload(refresh_token, config_str_secret_key_for_refresh_token)
|
1273
|
+
except Exception as error:
|
1274
|
+
output_content = get_api_output_in_standard_format(
|
1275
|
+
message=messages["INCORRECT_REFRESH_TOKEN"],
|
1276
|
+
log=str(error),
|
1277
|
+
)
|
1278
|
+
raise HTTPException(
|
1279
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1280
|
+
detail=output_content,
|
1281
|
+
)
|
1282
|
+
# ======================================================================================
|
1283
|
+
# NOTE: if refresh token has expired no need to delete it during this call
|
1284
|
+
# ======================================================================================
|
1285
|
+
"""
|
1286
|
+
main process
|
1287
|
+
"""
|
1288
|
+
# delete session for user
|
1289
|
+
global_object_square_database_helper.delete_rows_v0(
|
1290
|
+
database_name=global_string_database_name,
|
1291
|
+
schema_name=global_string_schema_name,
|
1292
|
+
table_name=UserSession.__tablename__,
|
1293
|
+
filters=FiltersV0(
|
1294
|
+
root={
|
1295
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1296
|
+
eq=refresh_token
|
1297
|
+
),
|
1298
|
+
}
|
1299
|
+
),
|
1300
|
+
)
|
1301
|
+
"""
|
1302
|
+
return value
|
1303
|
+
"""
|
1304
|
+
output_content = get_api_output_in_standard_format(
|
1305
|
+
message=messages["LOGOUT_SUCCESSFUL"],
|
1306
|
+
)
|
1307
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1308
|
+
except HTTPException as http_exception:
|
1309
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1310
|
+
return JSONResponse(
|
1311
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1312
|
+
)
|
1313
|
+
except Exception as e:
|
1314
|
+
"""
|
1315
|
+
rollback logic
|
1316
|
+
"""
|
1317
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1318
|
+
output_content = get_api_output_in_standard_format(
|
1319
|
+
message=messages["GENERIC_500"],
|
1320
|
+
log=str(e),
|
1321
|
+
)
|
1322
|
+
return JSONResponse(
|
1323
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1324
|
+
)
|
1325
|
+
|
1326
|
+
|
1327
|
+
@global_object_square_logger.auto_logger()
|
1328
|
+
def util_logout_apps_v0(access_token, app_ids):
|
1329
|
+
try:
|
1330
|
+
"""
|
1331
|
+
validation
|
1332
|
+
"""
|
1333
|
+
try:
|
1334
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1335
|
+
access_token, config_str_secret_key_for_access_token
|
1336
|
+
)
|
1337
|
+
except Exception as error:
|
1338
|
+
output_content = get_api_output_in_standard_format(
|
1339
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1340
|
+
)
|
1341
|
+
raise HTTPException(
|
1342
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1343
|
+
detail=output_content,
|
1344
|
+
)
|
1345
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1346
|
+
# validate app_ids
|
1347
|
+
app_ids = list(set(app_ids))
|
1348
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
1349
|
+
database_name=global_string_database_name,
|
1350
|
+
schema_name=global_string_schema_name,
|
1351
|
+
table_name=UserApp.__tablename__,
|
1352
|
+
filters=FiltersV0(
|
1353
|
+
root={
|
1354
|
+
UserApp.user_id.name: FilterConditionsV0(eq=user_id),
|
1355
|
+
}
|
1356
|
+
),
|
1357
|
+
columns=[UserApp.app_id.name],
|
1358
|
+
)["data"]["main"]
|
1359
|
+
local_list_user_app_ids = [
|
1360
|
+
x[UserApp.app_id.name] for x in local_list_response_user_app
|
1361
|
+
]
|
1362
|
+
local_list_invalid_app_ids = [
|
1363
|
+
x for x in app_ids if x not in local_list_user_app_ids
|
1364
|
+
]
|
1365
|
+
if len(local_list_invalid_app_ids) > 0:
|
1366
|
+
output_content = get_api_output_in_standard_format(
|
1367
|
+
message=messages["GENERIC_400"],
|
1368
|
+
log=f"invalid app_ids: {local_list_invalid_app_ids}.",
|
1369
|
+
)
|
1370
|
+
raise HTTPException(
|
1371
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1372
|
+
detail=output_content,
|
1373
|
+
)
|
1374
|
+
"""
|
1375
|
+
main process
|
1376
|
+
"""
|
1377
|
+
# delete session for user
|
1378
|
+
global_object_square_database_helper.delete_rows_v0(
|
1379
|
+
database_name=global_string_database_name,
|
1380
|
+
schema_name=global_string_schema_name,
|
1381
|
+
table_name=UserSession.__tablename__,
|
1382
|
+
filters=FiltersV0(
|
1383
|
+
root={
|
1384
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
1385
|
+
UserSession.app_id.name: FilterConditionsV0(in_=app_ids),
|
1386
|
+
}
|
1387
|
+
),
|
1388
|
+
)
|
1389
|
+
"""
|
1390
|
+
return value
|
1391
|
+
"""
|
1392
|
+
output_content = get_api_output_in_standard_format(
|
1393
|
+
message=messages["LOGOUT_SUCCESSFUL"],
|
1394
|
+
)
|
1395
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1396
|
+
except HTTPException as http_exception:
|
1397
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1398
|
+
return JSONResponse(
|
1399
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1400
|
+
)
|
1401
|
+
except Exception as e:
|
1402
|
+
"""
|
1403
|
+
rollback logic
|
1404
|
+
"""
|
1405
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1406
|
+
output_content = get_api_output_in_standard_format(
|
1407
|
+
message=messages["GENERIC_500"],
|
1408
|
+
log=str(e),
|
1409
|
+
)
|
1410
|
+
return JSONResponse(
|
1411
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1412
|
+
)
|
1413
|
+
|
1414
|
+
|
1415
|
+
@global_object_square_logger.auto_logger()
|
1416
|
+
def util_logout_all_v0(access_token):
|
1417
|
+
try:
|
1418
|
+
"""
|
1419
|
+
validation
|
1420
|
+
"""
|
1421
|
+
try:
|
1422
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1423
|
+
access_token, config_str_secret_key_for_access_token
|
1424
|
+
)
|
1425
|
+
except Exception as error:
|
1426
|
+
output_content = get_api_output_in_standard_format(
|
1427
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1428
|
+
)
|
1429
|
+
raise HTTPException(
|
1430
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1431
|
+
detail=output_content,
|
1432
|
+
)
|
1433
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1434
|
+
|
1435
|
+
"""
|
1436
|
+
main process
|
1437
|
+
"""
|
1438
|
+
# delete session for user
|
1439
|
+
global_object_square_database_helper.delete_rows_v0(
|
1440
|
+
database_name=global_string_database_name,
|
1441
|
+
schema_name=global_string_schema_name,
|
1442
|
+
table_name=UserSession.__tablename__,
|
1443
|
+
filters=FiltersV0(
|
1444
|
+
root={
|
1445
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
1446
|
+
}
|
1447
|
+
),
|
1448
|
+
)
|
1449
|
+
"""
|
1450
|
+
return value
|
1451
|
+
"""
|
1452
|
+
output_content = get_api_output_in_standard_format(
|
1453
|
+
message=messages["LOGOUT_SUCCESSFUL"],
|
1454
|
+
)
|
1455
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1456
|
+
except HTTPException as http_exception:
|
1457
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1458
|
+
return JSONResponse(
|
1459
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1460
|
+
)
|
1461
|
+
except Exception as e:
|
1462
|
+
"""
|
1463
|
+
rollback logic
|
1464
|
+
"""
|
1465
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1466
|
+
output_content = get_api_output_in_standard_format(
|
1467
|
+
message=messages["GENERIC_500"],
|
1468
|
+
log=str(e),
|
1469
|
+
)
|
1470
|
+
return JSONResponse(
|
1471
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1472
|
+
)
|
1473
|
+
|
1474
|
+
|
1475
|
+
@global_object_square_logger.auto_logger()
|
1476
|
+
def util_update_username_v0(new_username, access_token):
|
1477
|
+
try:
|
1478
|
+
"""
|
1479
|
+
validation
|
1480
|
+
"""
|
1481
|
+
# validate access token
|
1482
|
+
try:
|
1483
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1484
|
+
access_token, config_str_secret_key_for_access_token
|
1485
|
+
)
|
1486
|
+
except Exception as error:
|
1487
|
+
output_content = get_api_output_in_standard_format(
|
1488
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1489
|
+
)
|
1490
|
+
raise HTTPException(
|
1491
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1492
|
+
detail=output_content,
|
1493
|
+
)
|
1494
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1495
|
+
|
1496
|
+
# validation for username
|
1497
|
+
new_username = new_username.lower()
|
1498
|
+
username_pattern = re.compile(r"^[a-z0-9._-]{2,20}$")
|
1499
|
+
if not username_pattern.match(new_username):
|
1500
|
+
output_content = get_api_output_in_standard_format(
|
1501
|
+
message=messages["USERNAME_INVALID"],
|
1502
|
+
log=f"username '{new_username}' is invalid. it must start and end with a letter, "
|
1503
|
+
f"contain only lowercase letters, numbers, underscores, or hyphens, "
|
1504
|
+
f"and not have consecutive separators.",
|
1505
|
+
)
|
1506
|
+
raise HTTPException(
|
1507
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1508
|
+
detail=output_content,
|
1509
|
+
)
|
1510
|
+
|
1511
|
+
# validate user_id
|
1512
|
+
local_list_user_response = global_object_square_database_helper.get_rows_v0(
|
1513
|
+
database_name=global_string_database_name,
|
1514
|
+
schema_name=global_string_schema_name,
|
1515
|
+
table_name=User.__tablename__,
|
1516
|
+
filters=FiltersV0(
|
1517
|
+
root={
|
1518
|
+
User.user_id.name: FilterConditionsV0(eq=user_id),
|
1519
|
+
}
|
1520
|
+
),
|
1521
|
+
)["data"]["main"]
|
1522
|
+
|
1523
|
+
if len(local_list_user_response) != 1:
|
1524
|
+
output_content = get_api_output_in_standard_format(
|
1525
|
+
message=messages["INCORRECT_USER_ID"],
|
1526
|
+
log=f"incorrect user_id: {user_id}.",
|
1527
|
+
)
|
1528
|
+
raise HTTPException(
|
1529
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1530
|
+
detail=output_content,
|
1531
|
+
)
|
1532
|
+
|
1533
|
+
# validate new username
|
1534
|
+
local_list_user_credentials_response = (
|
1535
|
+
global_object_square_database_helper.get_rows_v0(
|
1536
|
+
database_name=global_string_database_name,
|
1537
|
+
schema_name=global_string_schema_name,
|
1538
|
+
table_name=User.__tablename__,
|
1539
|
+
filters=FiltersV0(
|
1540
|
+
root={
|
1541
|
+
User.user_username.name: FilterConditionsV0(eq=new_username),
|
1542
|
+
}
|
1543
|
+
),
|
1544
|
+
)["data"]["main"]
|
1545
|
+
)
|
1546
|
+
if len(local_list_user_credentials_response) != 0:
|
1547
|
+
output_content = get_api_output_in_standard_format(
|
1548
|
+
message=messages["USERNAME_ALREADY_EXISTS"],
|
1549
|
+
log=f"{new_username} is taken.",
|
1550
|
+
)
|
1551
|
+
raise HTTPException(
|
1552
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1553
|
+
detail=output_content,
|
1554
|
+
)
|
1555
|
+
"""
|
1556
|
+
main process
|
1557
|
+
"""
|
1558
|
+
# edit the username
|
1559
|
+
global_object_square_database_helper.edit_rows_v0(
|
1560
|
+
database_name=global_string_database_name,
|
1561
|
+
schema_name=global_string_schema_name,
|
1562
|
+
table_name=User.__tablename__,
|
1563
|
+
filters=FiltersV0(
|
1564
|
+
root={
|
1565
|
+
User.user_id.name: FilterConditionsV0(eq=user_id),
|
1566
|
+
}
|
1567
|
+
),
|
1568
|
+
data={
|
1569
|
+
User.user_username.name: new_username,
|
1570
|
+
},
|
1571
|
+
)
|
1572
|
+
"""
|
1573
|
+
return value
|
1574
|
+
"""
|
1575
|
+
output_content = get_api_output_in_standard_format(
|
1576
|
+
data={"main": {"user_id": user_id, "username": new_username}},
|
1577
|
+
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
1578
|
+
)
|
1579
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1580
|
+
except HTTPException as http_exception:
|
1581
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1582
|
+
return JSONResponse(
|
1583
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1584
|
+
)
|
1585
|
+
except Exception as e:
|
1586
|
+
"""
|
1587
|
+
rollback logic
|
1588
|
+
"""
|
1589
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1590
|
+
output_content = get_api_output_in_standard_format(
|
1591
|
+
message=messages["GENERIC_500"],
|
1592
|
+
log=str(e),
|
1593
|
+
)
|
1594
|
+
return JSONResponse(
|
1595
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1596
|
+
)
|
1597
|
+
|
1598
|
+
|
1599
|
+
@global_object_square_logger.auto_logger()
|
1600
|
+
def util_delete_user_v0(access_token, password):
|
1601
|
+
try:
|
1602
|
+
"""
|
1603
|
+
validation
|
1604
|
+
"""
|
1605
|
+
# validate access token
|
1606
|
+
try:
|
1607
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1608
|
+
access_token, config_str_secret_key_for_access_token
|
1609
|
+
)
|
1610
|
+
except Exception as error:
|
1611
|
+
output_content = get_api_output_in_standard_format(
|
1612
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1613
|
+
)
|
1614
|
+
raise HTTPException(
|
1615
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1616
|
+
detail=output_content,
|
1617
|
+
)
|
1618
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1619
|
+
|
1620
|
+
# validate user_id
|
1621
|
+
local_list_authentication_user_response = (
|
1622
|
+
global_object_square_database_helper.get_rows_v0(
|
1623
|
+
database_name=global_string_database_name,
|
1624
|
+
schema_name=global_string_schema_name,
|
1625
|
+
table_name=UserCredential.__tablename__,
|
1626
|
+
filters=FiltersV0(
|
1627
|
+
root={UserCredential.user_id.name: FilterConditionsV0(eq=user_id)}
|
1628
|
+
),
|
1629
|
+
)["data"]["main"]
|
1630
|
+
)
|
1631
|
+
if len(local_list_authentication_user_response) != 1:
|
1632
|
+
output_content = get_api_output_in_standard_format(
|
1633
|
+
message=messages["INCORRECT_USER_ID"],
|
1634
|
+
log=f"incorrect user_id: {user_id}.",
|
1635
|
+
)
|
1636
|
+
raise HTTPException(
|
1637
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1638
|
+
detail=output_content,
|
1639
|
+
)
|
1640
|
+
|
1641
|
+
# validate password
|
1642
|
+
local_dict_user = local_list_authentication_user_response[0]
|
1643
|
+
if not (
|
1644
|
+
bcrypt.checkpw(
|
1645
|
+
password.encode("utf-8"),
|
1646
|
+
local_dict_user[
|
1647
|
+
UserCredential.user_credential_hashed_password.name
|
1648
|
+
].encode("utf-8"),
|
1649
|
+
)
|
1650
|
+
):
|
1651
|
+
output_content = get_api_output_in_standard_format(
|
1652
|
+
message=messages["INCORRECT_PASSWORD"],
|
1653
|
+
log=f"incorrect password for user_id {user_id}.",
|
1654
|
+
)
|
1655
|
+
raise HTTPException(
|
1656
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1657
|
+
detail=output_content,
|
1658
|
+
)
|
1659
|
+
"""
|
1660
|
+
main process
|
1661
|
+
"""
|
1662
|
+
# fetch user profile photo storage token
|
1663
|
+
user_profile_storage_token = global_object_square_database_helper.get_rows_v0(
|
1664
|
+
database_name=global_string_database_name,
|
1665
|
+
schema_name=global_string_schema_name,
|
1666
|
+
table_name=UserProfile.__tablename__,
|
1667
|
+
filters=FiltersV0(
|
1668
|
+
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
1669
|
+
),
|
1670
|
+
columns=[UserProfile.user_profile_photo_storage_token.name],
|
1671
|
+
)["data"]["main"][0][UserProfile.user_profile_photo_storage_token.name]
|
1672
|
+
|
1673
|
+
# delete the user.
|
1674
|
+
global_object_square_database_helper.delete_rows_v0(
|
1675
|
+
database_name=global_string_database_name,
|
1676
|
+
schema_name=global_string_schema_name,
|
1677
|
+
table_name=User.__tablename__,
|
1678
|
+
filters=FiltersV0(
|
1679
|
+
root={
|
1680
|
+
User.user_id.name: FilterConditionsV0(eq=user_id),
|
1681
|
+
}
|
1682
|
+
),
|
1683
|
+
)
|
1684
|
+
# delete profile photo if exists
|
1685
|
+
if user_profile_storage_token:
|
1686
|
+
try:
|
1687
|
+
global_object_square_file_store_helper.delete_file_v0(
|
1688
|
+
list_file_storage_token=[user_profile_storage_token]
|
1689
|
+
)
|
1690
|
+
except HTTPError as he:
|
1691
|
+
global_object_square_logger.warning(
|
1692
|
+
f"Failed to delete user profile photo with storage token {user_profile_storage_token}. "
|
1693
|
+
f"Error: {he.response.text}",
|
1694
|
+
exc_info=True,
|
1695
|
+
)
|
1696
|
+
"""
|
1697
|
+
return value
|
1698
|
+
"""
|
1699
|
+
output_content = get_api_output_in_standard_format(
|
1700
|
+
message=messages["GENERIC_DELETE_SUCCESSFUL"],
|
1701
|
+
log=f"user_id: {user_id} deleted successfully.",
|
1702
|
+
)
|
1703
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1704
|
+
except HTTPException as http_exception:
|
1705
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1706
|
+
return JSONResponse(
|
1707
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1708
|
+
)
|
1709
|
+
except Exception as e:
|
1710
|
+
"""
|
1711
|
+
rollback logic
|
1712
|
+
"""
|
1713
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1714
|
+
output_content = get_api_output_in_standard_format(
|
1715
|
+
message=messages["GENERIC_500"],
|
1716
|
+
log=str(e),
|
1717
|
+
)
|
1718
|
+
return JSONResponse(
|
1719
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1720
|
+
)
|
1721
|
+
|
1722
|
+
|
1723
|
+
@global_object_square_logger.auto_logger()
|
1724
|
+
def util_update_password_v0(
|
1725
|
+
access_token,
|
1726
|
+
old_password,
|
1727
|
+
new_password,
|
1728
|
+
logout_other_sessions,
|
1729
|
+
preserve_session_refresh_token,
|
1730
|
+
):
|
1731
|
+
try:
|
1732
|
+
"""
|
1733
|
+
validation
|
1734
|
+
"""
|
1735
|
+
# validate access token
|
1736
|
+
try:
|
1737
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1738
|
+
access_token, config_str_secret_key_for_access_token
|
1739
|
+
)
|
1740
|
+
except Exception as error:
|
1741
|
+
output_content = get_api_output_in_standard_format(
|
1742
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1743
|
+
)
|
1744
|
+
raise HTTPException(
|
1745
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1746
|
+
detail=output_content,
|
1747
|
+
)
|
1748
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1749
|
+
|
1750
|
+
# validate user_id
|
1751
|
+
local_list_authentication_user_response = (
|
1752
|
+
global_object_square_database_helper.get_rows_v0(
|
1753
|
+
database_name=global_string_database_name,
|
1754
|
+
schema_name=global_string_schema_name,
|
1755
|
+
table_name=UserCredential.__tablename__,
|
1756
|
+
filters=FiltersV0(
|
1757
|
+
root={UserCredential.user_id.name: FilterConditionsV0(eq=user_id)}
|
1758
|
+
),
|
1759
|
+
)["data"]["main"]
|
1760
|
+
)
|
1761
|
+
if len(local_list_authentication_user_response) != 1:
|
1762
|
+
output_content = get_api_output_in_standard_format(
|
1763
|
+
message=messages["INCORRECT_USER_ID"],
|
1764
|
+
log=f"incorrect user_id: {user_id}.",
|
1765
|
+
)
|
1766
|
+
raise HTTPException(
|
1767
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1768
|
+
detail=output_content,
|
1769
|
+
)
|
1770
|
+
# check if user has SELF auth provider
|
1771
|
+
local_list_response_user_auth_provider = (
|
1772
|
+
global_object_square_database_helper.get_rows_v0(
|
1773
|
+
database_name=global_string_database_name,
|
1774
|
+
schema_name=global_string_schema_name,
|
1775
|
+
table_name=UserAuthProvider.__tablename__,
|
1776
|
+
filters=FiltersV0(
|
1777
|
+
root={
|
1778
|
+
UserAuthProvider.user_id.name: FilterConditionsV0(eq=user_id),
|
1779
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
1780
|
+
eq=AuthProviderEnum.SELF.value
|
1781
|
+
),
|
1782
|
+
}
|
1783
|
+
),
|
1784
|
+
)["data"]["main"]
|
1785
|
+
)
|
1786
|
+
if len(local_list_response_user_auth_provider) != 1:
|
1787
|
+
output_content = get_api_output_in_standard_format(
|
1788
|
+
message=messages["INCORRECT_AUTH_PROVIDER"],
|
1789
|
+
log=f"user_id: {user_id} does not have {AuthProviderEnum.SELF.value} auth provider.",
|
1790
|
+
)
|
1791
|
+
raise HTTPException(
|
1792
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1793
|
+
detail=output_content,
|
1794
|
+
)
|
1795
|
+
# check if user has credentials (might not be set in case of errors in registration.)
|
1796
|
+
local_list_response_user = global_object_square_database_helper.get_rows_v0(
|
1797
|
+
database_name=global_string_database_name,
|
1798
|
+
schema_name=global_string_schema_name,
|
1799
|
+
table_name=User.__tablename__,
|
1800
|
+
filters=FiltersV0(root={User.user_id.name: FilterConditionsV0(eq=user_id)}),
|
1801
|
+
)["data"]["main"]
|
1802
|
+
if len(local_list_response_user) != 1:
|
1803
|
+
output_content = get_api_output_in_standard_format(
|
1804
|
+
message=messages["MALFORMED_USER"],
|
1805
|
+
log=f"user_id: {user_id} does not have credentials.",
|
1806
|
+
)
|
1807
|
+
raise HTTPException(
|
1808
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1809
|
+
detail=output_content,
|
1810
|
+
)
|
1811
|
+
# validate password
|
1812
|
+
local_dict_user = local_list_authentication_user_response[0]
|
1813
|
+
if not (
|
1814
|
+
bcrypt.checkpw(
|
1815
|
+
old_password.encode("utf-8"),
|
1816
|
+
local_dict_user[
|
1817
|
+
UserCredential.user_credential_hashed_password.name
|
1818
|
+
].encode("utf-8"),
|
1819
|
+
)
|
1820
|
+
):
|
1821
|
+
output_content = get_api_output_in_standard_format(
|
1822
|
+
message=messages["INCORRECT_PASSWORD"],
|
1823
|
+
log=f"incorrect password for user_id {user_id}.",
|
1824
|
+
)
|
1825
|
+
raise HTTPException(
|
1826
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1827
|
+
detail=output_content,
|
1828
|
+
)
|
1829
|
+
# check if provided refresh token is valid
|
1830
|
+
if preserve_session_refresh_token:
|
1831
|
+
local_dict_token_payload = get_jwt_payload(
|
1832
|
+
preserve_session_refresh_token, config_str_secret_key_for_refresh_token
|
1833
|
+
)
|
1834
|
+
local_list_response_user_session = global_object_square_database_helper.get_rows_v0(
|
1835
|
+
database_name=global_string_database_name,
|
1836
|
+
schema_name=global_string_schema_name,
|
1837
|
+
table_name=UserSession.__tablename__,
|
1838
|
+
filters=FiltersV0(
|
1839
|
+
root={
|
1840
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
1841
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1842
|
+
eq=preserve_session_refresh_token
|
1843
|
+
),
|
1844
|
+
}
|
1845
|
+
),
|
1846
|
+
)[
|
1847
|
+
"data"
|
1848
|
+
][
|
1849
|
+
"main"
|
1850
|
+
]
|
1851
|
+
if len(local_list_response_user_session) != 1:
|
1852
|
+
output_content = get_api_output_in_standard_format(
|
1853
|
+
message=messages["INCORRECT_REFRESH_TOKEN"],
|
1854
|
+
log=f"incorrect refresh token: {preserve_session_refresh_token}.",
|
1855
|
+
)
|
1856
|
+
raise HTTPException(
|
1857
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1858
|
+
detail=output_content,
|
1859
|
+
)
|
1860
|
+
"""
|
1861
|
+
main process
|
1862
|
+
"""
|
1863
|
+
# update the password
|
1864
|
+
local_str_hashed_password = bcrypt.hashpw(
|
1865
|
+
new_password.encode("utf-8"), bcrypt.gensalt()
|
1866
|
+
).decode("utf-8")
|
1867
|
+
global_object_square_database_helper.edit_rows_v0(
|
1868
|
+
database_name=global_string_database_name,
|
1869
|
+
schema_name=global_string_schema_name,
|
1870
|
+
table_name=UserCredential.__tablename__,
|
1871
|
+
filters=FiltersV0(
|
1872
|
+
root={
|
1873
|
+
UserCredential.user_id.name: FilterConditionsV0(eq=user_id),
|
1874
|
+
}
|
1875
|
+
),
|
1876
|
+
data={
|
1877
|
+
UserCredential.user_credential_hashed_password.name: local_str_hashed_password,
|
1878
|
+
},
|
1879
|
+
)
|
1880
|
+
if logout_other_sessions:
|
1881
|
+
if preserve_session_refresh_token:
|
1882
|
+
# delete all sessions for user except the one with the given refresh token
|
1883
|
+
global_object_square_database_helper.delete_rows_v0(
|
1884
|
+
database_name=global_string_database_name,
|
1885
|
+
schema_name=global_string_schema_name,
|
1886
|
+
table_name=UserSession.__tablename__,
|
1887
|
+
filters=FiltersV0(
|
1888
|
+
root={
|
1889
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
1890
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1891
|
+
ne=preserve_session_refresh_token
|
1892
|
+
),
|
1893
|
+
}
|
1894
|
+
),
|
1895
|
+
)
|
1896
|
+
else:
|
1897
|
+
# delete all sessions for user
|
1898
|
+
global_object_square_database_helper.delete_rows_v0(
|
1899
|
+
database_name=global_string_database_name,
|
1900
|
+
schema_name=global_string_schema_name,
|
1901
|
+
table_name=UserSession.__tablename__,
|
1902
|
+
filters=FiltersV0(
|
1903
|
+
root={
|
1904
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
1905
|
+
}
|
1906
|
+
),
|
1907
|
+
)
|
1908
|
+
"""
|
1909
|
+
return value
|
1910
|
+
"""
|
1911
|
+
output_content = get_api_output_in_standard_format(
|
1912
|
+
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
1913
|
+
log=f"password for user_id: {user_id} updated successfully.",
|
1914
|
+
)
|
1915
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
1916
|
+
except HTTPException as http_exception:
|
1917
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1918
|
+
return JSONResponse(
|
1919
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1920
|
+
)
|
1921
|
+
except Exception as e:
|
1922
|
+
"""
|
1923
|
+
rollback logic
|
1924
|
+
"""
|
1925
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1926
|
+
output_content = get_api_output_in_standard_format(
|
1927
|
+
message=messages["GENERIC_500"],
|
1928
|
+
log=str(e),
|
1929
|
+
)
|
1930
|
+
return JSONResponse(
|
1931
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1932
|
+
)
|
1933
|
+
|
1934
|
+
|
1935
|
+
@global_object_square_logger.auto_logger()
|
1936
|
+
def util_validate_and_get_payload_from_token_v0(app_id, token, token_type):
|
1937
|
+
try:
|
1938
|
+
"""
|
1939
|
+
validation
|
1940
|
+
"""
|
1941
|
+
# validate token
|
1942
|
+
try:
|
1943
|
+
local_dict_token_payload = None
|
1944
|
+
if token_type == TokenType.access_token:
|
1945
|
+
local_dict_token_payload = get_jwt_payload(
|
1946
|
+
token, config_str_secret_key_for_access_token
|
1947
|
+
)
|
1948
|
+
elif token_type == TokenType.refresh_token:
|
1949
|
+
local_dict_token_payload = get_jwt_payload(
|
1950
|
+
token, config_str_secret_key_for_refresh_token
|
1951
|
+
)
|
1952
|
+
local_list_response_user_session = global_object_square_database_helper.get_rows_v0(
|
1953
|
+
database_name=global_string_database_name,
|
1954
|
+
schema_name=global_string_schema_name,
|
1955
|
+
table_name=UserSession.__tablename__,
|
1956
|
+
filters=FiltersV0(
|
1957
|
+
root={
|
1958
|
+
UserSession.user_session_refresh_token.name: FilterConditionsV0(
|
1959
|
+
eq=token
|
1960
|
+
),
|
1961
|
+
UserSession.user_id.name: FilterConditionsV0(
|
1962
|
+
eq=local_dict_token_payload["user_id"]
|
1963
|
+
),
|
1964
|
+
}
|
1965
|
+
),
|
1966
|
+
)[
|
1967
|
+
"data"
|
1968
|
+
][
|
1969
|
+
"main"
|
1970
|
+
]
|
1971
|
+
if len(local_list_response_user_session) != 1:
|
1972
|
+
output_content = get_api_output_in_standard_format(
|
1973
|
+
message=messages["INCORRECT_REFRESH_TOKEN"],
|
1974
|
+
log="refresh token valid but not present in database.",
|
1975
|
+
)
|
1976
|
+
raise HTTPException(
|
1977
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1978
|
+
detail=output_content,
|
1979
|
+
)
|
1980
|
+
if local_dict_token_payload["app_id"] != app_id:
|
1981
|
+
output_content = get_api_output_in_standard_format(
|
1982
|
+
message=messages["GENERIC_400"],
|
1983
|
+
log=f"app_id: {app_id} does not match with token app_id: {local_dict_token_payload['app_id']}.",
|
1984
|
+
)
|
1985
|
+
raise HTTPException(
|
1986
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1987
|
+
detail=output_content,
|
1988
|
+
)
|
1989
|
+
except HTTPException as http_exception:
|
1990
|
+
raise
|
1991
|
+
except Exception as error:
|
1992
|
+
output_content = None
|
1993
|
+
if token_type == TokenType.access_token:
|
1994
|
+
output_content = get_api_output_in_standard_format(
|
1995
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1996
|
+
)
|
1997
|
+
elif token_type == TokenType.refresh_token:
|
1998
|
+
output_content = get_api_output_in_standard_format(
|
1999
|
+
message=messages["INCORRECT_REFRESH_TOKEN"], log=str(error)
|
2000
|
+
)
|
2001
|
+
|
2002
|
+
raise HTTPException(
|
2003
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2004
|
+
detail=output_content,
|
2005
|
+
)
|
2006
|
+
|
2007
|
+
"""
|
2008
|
+
main process
|
2009
|
+
"""
|
2010
|
+
# pass
|
2011
|
+
"""
|
2012
|
+
return value
|
2013
|
+
"""
|
2014
|
+
output_content = get_api_output_in_standard_format(
|
2015
|
+
message=messages["GENERIC_READ_SUCCESSFUL"],
|
2016
|
+
data={"main": local_dict_token_payload},
|
2017
|
+
)
|
2018
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
2019
|
+
except HTTPException as http_exception:
|
2020
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
2021
|
+
return JSONResponse(
|
2022
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
2023
|
+
)
|
2024
|
+
except Exception as e:
|
2025
|
+
"""
|
2026
|
+
rollback logic
|
2027
|
+
"""
|
2028
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
2029
|
+
output_content = get_api_output_in_standard_format(
|
2030
|
+
message=messages["GENERIC_500"],
|
2031
|
+
log=str(e),
|
2032
|
+
)
|
2033
|
+
return JSONResponse(
|
2034
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
2035
|
+
)
|
2036
|
+
|
2037
|
+
|
2038
|
+
@global_object_square_logger.auto_logger()
|
2039
|
+
def util_update_user_recovery_methods_v0(
|
2040
|
+
access_token, recovery_methods_to_add, recovery_methods_to_remove
|
2041
|
+
):
|
2042
|
+
if recovery_methods_to_add is None:
|
2043
|
+
recovery_methods_to_add = []
|
2044
|
+
if recovery_methods_to_remove is None:
|
2045
|
+
recovery_methods_to_remove = []
|
2046
|
+
try:
|
2047
|
+
|
2048
|
+
"""
|
2049
|
+
validation
|
2050
|
+
"""
|
2051
|
+
# validate access token
|
2052
|
+
try:
|
2053
|
+
local_dict_access_token_payload = get_jwt_payload(
|
2054
|
+
access_token, config_str_secret_key_for_access_token
|
2055
|
+
)
|
2056
|
+
except Exception as error:
|
2057
|
+
output_content = get_api_output_in_standard_format(
|
2058
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
2059
|
+
)
|
2060
|
+
raise HTTPException(
|
2061
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2062
|
+
detail=output_content,
|
2063
|
+
)
|
2064
|
+
user_id = local_dict_access_token_payload["user_id"]
|
2065
|
+
|
2066
|
+
recovery_methods_to_add = list(set(x.value for x in recovery_methods_to_add))
|
2067
|
+
recovery_methods_to_remove = list(
|
2068
|
+
set(x.value for x in recovery_methods_to_remove)
|
2069
|
+
)
|
2070
|
+
|
2071
|
+
# check if recovery_methods_to_add and recovery_methods_to_remove don't have common ids.
|
2072
|
+
local_list_common_recovery_methods = set(recovery_methods_to_add) & set(
|
2073
|
+
recovery_methods_to_remove
|
2074
|
+
)
|
2075
|
+
if len(local_list_common_recovery_methods) > 0:
|
2076
|
+
output_content = get_api_output_in_standard_format(
|
2077
|
+
message=messages["GENERIC_400"],
|
2078
|
+
log=f"invalid recovery_methods: {list(local_list_common_recovery_methods)}, present in both add list and remove list.",
|
2079
|
+
)
|
2080
|
+
raise HTTPException(
|
2081
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2082
|
+
detail=output_content,
|
2083
|
+
)
|
2084
|
+
# check if user's email is verified in user profile.
|
2085
|
+
# maybe too harsh to reject the request entirely,
|
2086
|
+
# but for practical purposes this api call should be used for 1 recovery method at a time, so it's not too bad.
|
2087
|
+
if RecoveryMethodEnum.EMAIL.value in recovery_methods_to_add:
|
2088
|
+
local_list_response_user_profile = (
|
2089
|
+
global_object_square_database_helper.get_rows_v0(
|
2090
|
+
database_name=global_string_database_name,
|
2091
|
+
schema_name=global_string_schema_name,
|
2092
|
+
table_name=UserProfile.__tablename__,
|
2093
|
+
filters=FiltersV0(
|
2094
|
+
root={
|
2095
|
+
UserProfile.user_id.name: FilterConditionsV0(eq=user_id),
|
2096
|
+
}
|
2097
|
+
),
|
2098
|
+
)["data"]["main"]
|
2099
|
+
)
|
2100
|
+
if len(local_list_response_user_profile) != 1:
|
2101
|
+
# maybe this should raise 500 as this error will not occur if code runs correctly.
|
2102
|
+
output_content = get_api_output_in_standard_format(
|
2103
|
+
message=messages["GENERIC_400"],
|
2104
|
+
log=f"user_id: {user_id} does not have a profile.",
|
2105
|
+
)
|
2106
|
+
raise HTTPException(
|
2107
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2108
|
+
detail=output_content,
|
2109
|
+
)
|
2110
|
+
local_dict_user_profile = local_list_response_user_profile[0]
|
2111
|
+
if not local_dict_user_profile[
|
2112
|
+
UserProfile.user_profile_email_verified.name
|
2113
|
+
]:
|
2114
|
+
output_content = get_api_output_in_standard_format(
|
2115
|
+
message=messages["EMAIL_NOT_VERIFIED"],
|
2116
|
+
log=f"user_id: {user_id} does not have email verified.",
|
2117
|
+
)
|
2118
|
+
raise HTTPException(
|
2119
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2120
|
+
detail=output_content,
|
2121
|
+
)
|
2122
|
+
"""
|
2123
|
+
main process
|
2124
|
+
"""
|
2125
|
+
# logic for adding new recovery_methods
|
2126
|
+
local_list_response_user_recovery_methods = (
|
2127
|
+
global_object_square_database_helper.get_rows_v0(
|
2128
|
+
database_name=global_string_database_name,
|
2129
|
+
schema_name=global_string_schema_name,
|
2130
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2131
|
+
filters=FiltersV0(
|
2132
|
+
root={
|
2133
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id)
|
2134
|
+
}
|
2135
|
+
),
|
2136
|
+
)["data"]["main"]
|
2137
|
+
)
|
2138
|
+
local_list_new_recovery_methods = [
|
2139
|
+
{
|
2140
|
+
UserRecoveryMethod.user_id.name: user_id,
|
2141
|
+
UserRecoveryMethod.user_recovery_method_name.name: x,
|
2142
|
+
}
|
2143
|
+
for x in recovery_methods_to_add
|
2144
|
+
if x
|
2145
|
+
not in [
|
2146
|
+
y[UserRecoveryMethod.user_recovery_method_name.name]
|
2147
|
+
for y in local_list_response_user_recovery_methods
|
2148
|
+
]
|
2149
|
+
]
|
2150
|
+
if len(local_list_new_recovery_methods) > 0:
|
2151
|
+
global_object_square_database_helper.insert_rows_v0(
|
2152
|
+
database_name=global_string_database_name,
|
2153
|
+
schema_name=global_string_schema_name,
|
2154
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2155
|
+
data=local_list_new_recovery_methods,
|
2156
|
+
)
|
2157
|
+
|
2158
|
+
# logic for removing recovery_methods
|
2159
|
+
remove_old_backup_codes = (
|
2160
|
+
RecoveryMethodEnum.BACKUP_CODE.value in recovery_methods_to_remove
|
2161
|
+
)
|
2162
|
+
old_backup_code_hashes = None
|
2163
|
+
if remove_old_backup_codes:
|
2164
|
+
# delete existing backup codes if any
|
2165
|
+
old_backup_code_hashes = global_object_square_database_helper.get_rows_v0(
|
2166
|
+
database_name=global_string_database_name,
|
2167
|
+
schema_name=global_string_schema_name,
|
2168
|
+
table_name=UserVerificationCode.__tablename__,
|
2169
|
+
filters=FiltersV0(
|
2170
|
+
root={
|
2171
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(
|
2172
|
+
eq=user_id
|
2173
|
+
),
|
2174
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
2175
|
+
eq=VerificationCodeTypeEnum.BACKUP_CODE_RECOVERY.value
|
2176
|
+
),
|
2177
|
+
}
|
2178
|
+
),
|
2179
|
+
columns=[UserVerificationCode.user_verification_code_hash.name],
|
2180
|
+
)["data"]["main"]
|
2181
|
+
global_object_square_database_helper.delete_rows_v0(
|
2182
|
+
database_name=global_string_database_name,
|
2183
|
+
schema_name=global_string_schema_name,
|
2184
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2185
|
+
filters=FiltersV0(
|
2186
|
+
root={
|
2187
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
2188
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
2189
|
+
in_=recovery_methods_to_remove
|
2190
|
+
),
|
2191
|
+
}
|
2192
|
+
),
|
2193
|
+
)
|
2194
|
+
if remove_old_backup_codes and old_backup_code_hashes:
|
2195
|
+
global_object_square_database_helper.delete_rows_v0(
|
2196
|
+
database_name=global_string_database_name,
|
2197
|
+
schema_name=global_string_schema_name,
|
2198
|
+
table_name=UserVerificationCode.__tablename__,
|
2199
|
+
filters=FiltersV0(
|
2200
|
+
root={
|
2201
|
+
UserVerificationCode.user_verification_code_hash.name: FilterConditionsV0(
|
2202
|
+
in_=[
|
2203
|
+
x[UserVerificationCode.user_verification_code_hash.name]
|
2204
|
+
for x in old_backup_code_hashes
|
2205
|
+
]
|
2206
|
+
),
|
2207
|
+
}
|
2208
|
+
),
|
2209
|
+
)
|
2210
|
+
|
2211
|
+
"""
|
2212
|
+
return value
|
2213
|
+
"""
|
2214
|
+
# get latest recovery_methods
|
2215
|
+
local_list_response_user_recovery_methods = (
|
2216
|
+
global_object_square_database_helper.get_rows_v0(
|
2217
|
+
database_name=global_string_database_name,
|
2218
|
+
schema_name=global_string_schema_name,
|
2219
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2220
|
+
filters=FiltersV0(
|
2221
|
+
root={
|
2222
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id)
|
2223
|
+
}
|
2224
|
+
),
|
2225
|
+
)["data"]["main"]
|
2226
|
+
)
|
2227
|
+
output_content = get_api_output_in_standard_format(
|
2228
|
+
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
2229
|
+
data={
|
2230
|
+
"main": [
|
2231
|
+
x[UserRecoveryMethod.user_recovery_method_name.name]
|
2232
|
+
for x in local_list_response_user_recovery_methods
|
2233
|
+
]
|
2234
|
+
},
|
2235
|
+
)
|
2236
|
+
return JSONResponse(
|
2237
|
+
status_code=status.HTTP_200_OK,
|
2238
|
+
content=output_content,
|
2239
|
+
)
|
2240
|
+
except HTTPException as http_exception:
|
2241
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
2242
|
+
return JSONResponse(
|
2243
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
2244
|
+
)
|
2245
|
+
except Exception as e:
|
2246
|
+
"""
|
2247
|
+
rollback logic
|
2248
|
+
"""
|
2249
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
2250
|
+
output_content = get_api_output_in_standard_format(
|
2251
|
+
message=messages["GENERIC_500"],
|
2252
|
+
log=str(e),
|
2253
|
+
)
|
2254
|
+
return JSONResponse(
|
2255
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
2256
|
+
content=output_content,
|
2257
|
+
)
|
2258
|
+
|
2259
|
+
|
2260
|
+
@global_object_square_logger.auto_logger()
|
2261
|
+
def util_generate_account_backup_codes_v0(access_token):
|
2262
|
+
try:
|
2263
|
+
"""
|
2264
|
+
validation
|
2265
|
+
"""
|
2266
|
+
try:
|
2267
|
+
local_dict_access_token_payload = get_jwt_payload(
|
2268
|
+
access_token, config_str_secret_key_for_access_token
|
2269
|
+
)
|
2270
|
+
except Exception as error:
|
2271
|
+
output_content = get_api_output_in_standard_format(
|
2272
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
2273
|
+
)
|
2274
|
+
raise HTTPException(
|
2275
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2276
|
+
detail=output_content,
|
2277
|
+
)
|
2278
|
+
user_id = local_dict_access_token_payload["user_id"]
|
2279
|
+
# check if user has recovery method enabled
|
2280
|
+
local_list_response_user_recovery_methods = global_object_square_database_helper.get_rows_v0(
|
2281
|
+
database_name=global_string_database_name,
|
2282
|
+
schema_name=global_string_schema_name,
|
2283
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2284
|
+
filters=FiltersV0(
|
2285
|
+
root={
|
2286
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
2287
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
2288
|
+
eq=RecoveryMethodEnum.BACKUP_CODE.value
|
2289
|
+
),
|
2290
|
+
}
|
2291
|
+
),
|
2292
|
+
)[
|
2293
|
+
"data"
|
2294
|
+
][
|
2295
|
+
"main"
|
2296
|
+
]
|
2297
|
+
if len(local_list_response_user_recovery_methods) != 1:
|
2298
|
+
output_content = get_api_output_in_standard_format(
|
2299
|
+
message=messages["RECOVERY_METHOD_NOT_ENABLED"],
|
2300
|
+
log=f"user_id: {user_id} does not have backup codes recovery method enabled.",
|
2301
|
+
)
|
2302
|
+
raise HTTPException(
|
2303
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2304
|
+
detail=output_content,
|
2305
|
+
)
|
2306
|
+
"""
|
2307
|
+
main process
|
2308
|
+
"""
|
2309
|
+
# delete existing backup codes if any
|
2310
|
+
old_backup_code_hashes = global_object_square_database_helper.get_rows_v0(
|
2311
|
+
database_name=global_string_database_name,
|
2312
|
+
schema_name=global_string_schema_name,
|
2313
|
+
table_name=UserVerificationCode.__tablename__,
|
2314
|
+
filters=FiltersV0(
|
2315
|
+
root={
|
2316
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
2317
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
2318
|
+
eq=VerificationCodeTypeEnum.BACKUP_CODE_RECOVERY.value
|
2319
|
+
),
|
2320
|
+
}
|
2321
|
+
),
|
2322
|
+
columns=[UserVerificationCode.user_verification_code_hash.name],
|
2323
|
+
)["data"]["main"]
|
2324
|
+
|
2325
|
+
# generate backup codes
|
2326
|
+
backup_codes = []
|
2327
|
+
db_data = []
|
2328
|
+
|
2329
|
+
for i in range(NUMBER_OF_RECOVERY_CODES):
|
2330
|
+
backup_code = str(uuid.uuid4())
|
2331
|
+
backup_codes.append(backup_code)
|
2332
|
+
# hash the backup code
|
2333
|
+
local_str_hashed_backup_code = bcrypt.hashpw(
|
2334
|
+
backup_code.encode("utf-8"), bcrypt.gensalt()
|
2335
|
+
).decode("utf-8")
|
2336
|
+
|
2337
|
+
db_data.append(
|
2338
|
+
{
|
2339
|
+
UserVerificationCode.user_id.name: user_id,
|
2340
|
+
UserVerificationCode.user_verification_code_type.name: VerificationCodeTypeEnum.BACKUP_CODE_RECOVERY.value,
|
2341
|
+
UserVerificationCode.user_verification_code_hash.name: local_str_hashed_backup_code,
|
2342
|
+
}
|
2343
|
+
)
|
2344
|
+
global_object_square_database_helper.insert_rows_v0(
|
2345
|
+
database_name=global_string_database_name,
|
2346
|
+
schema_name=global_string_schema_name,
|
2347
|
+
table_name=UserVerificationCode.__tablename__,
|
2348
|
+
data=db_data,
|
2349
|
+
)
|
2350
|
+
global_object_square_database_helper.delete_rows_v0(
|
2351
|
+
database_name=global_string_database_name,
|
2352
|
+
schema_name=global_string_schema_name,
|
2353
|
+
table_name=UserVerificationCode.__tablename__,
|
2354
|
+
filters=FiltersV0(
|
2355
|
+
root={
|
2356
|
+
UserVerificationCode.user_verification_code_hash.name: FilterConditionsV0(
|
2357
|
+
in_=[
|
2358
|
+
x[UserVerificationCode.user_verification_code_hash.name]
|
2359
|
+
for x in old_backup_code_hashes
|
2360
|
+
]
|
2361
|
+
),
|
2362
|
+
}
|
2363
|
+
),
|
2364
|
+
)
|
2365
|
+
"""
|
2366
|
+
return value
|
2367
|
+
"""
|
2368
|
+
output_content = get_api_output_in_standard_format(
|
2369
|
+
message=messages["GENERIC_CREATION_SUCCESSFUL"],
|
2370
|
+
data={
|
2371
|
+
"main": {
|
2372
|
+
"user_id": user_id,
|
2373
|
+
"backup_codes": backup_codes,
|
2374
|
+
}
|
2375
|
+
},
|
2376
|
+
)
|
2377
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
2378
|
+
except HTTPException as http_exception:
|
2379
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
2380
|
+
return JSONResponse(
|
2381
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
2382
|
+
)
|
2383
|
+
except Exception as e:
|
2384
|
+
"""
|
2385
|
+
rollback logic
|
2386
|
+
"""
|
2387
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
2388
|
+
output_content = get_api_output_in_standard_format(
|
2389
|
+
message=messages["GENERIC_500"],
|
2390
|
+
log=str(e),
|
2391
|
+
)
|
2392
|
+
return JSONResponse(
|
2393
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
2394
|
+
)
|
2395
|
+
|
2396
|
+
|
2397
|
+
@global_object_square_logger.auto_logger()
|
2398
|
+
def util_reset_password_and_login_using_backup_code_v0(
|
2399
|
+
backup_code, username, new_password, app_id, logout_other_sessions
|
2400
|
+
):
|
2401
|
+
try:
|
2402
|
+
"""
|
2403
|
+
validation
|
2404
|
+
"""
|
2405
|
+
# validate username
|
2406
|
+
local_list_authentication_user_response = (
|
2407
|
+
global_object_square_database_helper.get_rows_v0(
|
2408
|
+
database_name=global_string_database_name,
|
2409
|
+
schema_name=global_string_schema_name,
|
2410
|
+
table_name=User.__tablename__,
|
2411
|
+
filters=FiltersV0(
|
2412
|
+
root={User.user_username.name: FilterConditionsV0(eq=username)}
|
2413
|
+
),
|
2414
|
+
)["data"]["main"]
|
2415
|
+
)
|
2416
|
+
if len(local_list_authentication_user_response) != 1:
|
2417
|
+
output_content = get_api_output_in_standard_format(
|
2418
|
+
message=messages["INCORRECT_USERNAME"],
|
2419
|
+
log=f"incorrect username: {username}.",
|
2420
|
+
)
|
2421
|
+
raise HTTPException(
|
2422
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2423
|
+
detail=output_content,
|
2424
|
+
)
|
2425
|
+
user_id = local_list_authentication_user_response[0][User.user_id.name]
|
2426
|
+
# check if user has SELF auth provider
|
2427
|
+
local_list_response_user_auth_provider = (
|
2428
|
+
global_object_square_database_helper.get_rows_v0(
|
2429
|
+
database_name=global_string_database_name,
|
2430
|
+
schema_name=global_string_schema_name,
|
2431
|
+
table_name=UserAuthProvider.__tablename__,
|
2432
|
+
filters=FiltersV0(
|
2433
|
+
root={
|
2434
|
+
UserAuthProvider.user_id.name: FilterConditionsV0(eq=user_id),
|
2435
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
2436
|
+
eq=AuthProviderEnum.SELF.value
|
2437
|
+
),
|
2438
|
+
}
|
2439
|
+
),
|
2440
|
+
)["data"]["main"]
|
2441
|
+
)
|
2442
|
+
if len(local_list_response_user_auth_provider) != 1:
|
2443
|
+
output_content = get_api_output_in_standard_format(
|
2444
|
+
message=messages["INCORRECT_AUTH_PROVIDER"],
|
2445
|
+
log=f"user_id: {user_id} does not have {AuthProviderEnum.SELF.value} auth provider.",
|
2446
|
+
)
|
2447
|
+
raise HTTPException(
|
2448
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2449
|
+
detail=output_content,
|
2450
|
+
)
|
2451
|
+
# check if user has recovery method enabled
|
2452
|
+
local_list_response_user_recovery_methods = global_object_square_database_helper.get_rows_v0(
|
2453
|
+
database_name=global_string_database_name,
|
2454
|
+
schema_name=global_string_schema_name,
|
2455
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2456
|
+
filters=FiltersV0(
|
2457
|
+
root={
|
2458
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
2459
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
2460
|
+
eq=RecoveryMethodEnum.BACKUP_CODE.value
|
2461
|
+
),
|
2462
|
+
}
|
2463
|
+
),
|
2464
|
+
)[
|
2465
|
+
"data"
|
2466
|
+
][
|
2467
|
+
"main"
|
2468
|
+
]
|
2469
|
+
if len(local_list_response_user_recovery_methods) != 1:
|
2470
|
+
output_content = get_api_output_in_standard_format(
|
2471
|
+
message=messages["RECOVERY_METHOD_NOT_ENABLED"],
|
2472
|
+
log=f"user_id: {user_id} does not have backup codes recovery method enabled.",
|
2473
|
+
)
|
2474
|
+
raise HTTPException(
|
2475
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2476
|
+
detail=output_content,
|
2477
|
+
)
|
2478
|
+
# validate if user is assigned to the app.
|
2479
|
+
# not checking [skipping] if the app exists, as it is not required for this endpoint.
|
2480
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
2481
|
+
database_name=global_string_database_name,
|
2482
|
+
schema_name=global_string_schema_name,
|
2483
|
+
table_name=UserApp.__tablename__,
|
2484
|
+
filters=FiltersV0(
|
2485
|
+
root={
|
2486
|
+
UserApp.user_id.name: FilterConditionsV0(eq=user_id),
|
2487
|
+
UserApp.app_id.name: FilterConditionsV0(eq=app_id),
|
2488
|
+
}
|
2489
|
+
),
|
2490
|
+
)["data"]["main"]
|
2491
|
+
if len(local_list_response_user_app) == 0:
|
2492
|
+
output_content = get_api_output_in_standard_format(
|
2493
|
+
message=messages["GENERIC_400"],
|
2494
|
+
log=f"user_id: {user_id} is not assigned to app_id: {app_id}.",
|
2495
|
+
)
|
2496
|
+
raise HTTPException(
|
2497
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2498
|
+
detail=output_content,
|
2499
|
+
)
|
2500
|
+
"""
|
2501
|
+
main process
|
2502
|
+
"""
|
2503
|
+
# validate backup code
|
2504
|
+
local_list_response_user_verification_code = global_object_square_database_helper.get_rows_v0(
|
2505
|
+
database_name=global_string_database_name,
|
2506
|
+
schema_name=global_string_schema_name,
|
2507
|
+
table_name=UserVerificationCode.__tablename__,
|
2508
|
+
filters=FiltersV0(
|
2509
|
+
root={
|
2510
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
2511
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
2512
|
+
eq=VerificationCodeTypeEnum.BACKUP_CODE_RECOVERY.value
|
2513
|
+
),
|
2514
|
+
UserVerificationCode.user_verification_code_expires_at.name: FilterConditionsV0(
|
2515
|
+
is_null=True
|
2516
|
+
),
|
2517
|
+
UserVerificationCode.user_verification_code_used_at.name: FilterConditionsV0(
|
2518
|
+
is_null=True
|
2519
|
+
),
|
2520
|
+
}
|
2521
|
+
),
|
2522
|
+
columns=[UserVerificationCode.user_verification_code_hash.name],
|
2523
|
+
)[
|
2524
|
+
"data"
|
2525
|
+
][
|
2526
|
+
"main"
|
2527
|
+
]
|
2528
|
+
# find the backup code in the list
|
2529
|
+
local_list_response_user_verification_code = [
|
2530
|
+
x
|
2531
|
+
for x in local_list_response_user_verification_code
|
2532
|
+
if bcrypt.checkpw(
|
2533
|
+
backup_code.encode("utf-8"),
|
2534
|
+
x[UserVerificationCode.user_verification_code_hash.name].encode(
|
2535
|
+
"utf-8"
|
2536
|
+
),
|
2537
|
+
)
|
2538
|
+
]
|
2539
|
+
if len(local_list_response_user_verification_code) != 1:
|
2540
|
+
output_content = get_api_output_in_standard_format(
|
2541
|
+
message=messages["INCORRECT_BACKUP_CODE"],
|
2542
|
+
log=f"incorrect backup code: {backup_code} for user_id: {user_id}.",
|
2543
|
+
)
|
2544
|
+
raise HTTPException(
|
2545
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2546
|
+
detail=output_content,
|
2547
|
+
)
|
2548
|
+
# hash the new password
|
2549
|
+
local_str_hashed_password = bcrypt.hashpw(
|
2550
|
+
new_password.encode("utf-8"), bcrypt.gensalt()
|
2551
|
+
).decode("utf-8")
|
2552
|
+
# update the password
|
2553
|
+
global_object_square_database_helper.edit_rows_v0(
|
2554
|
+
database_name=global_string_database_name,
|
2555
|
+
schema_name=global_string_schema_name,
|
2556
|
+
table_name=UserCredential.__tablename__,
|
2557
|
+
filters=FiltersV0(
|
2558
|
+
root={
|
2559
|
+
UserCredential.user_id.name: FilterConditionsV0(eq=user_id),
|
2560
|
+
}
|
2561
|
+
),
|
2562
|
+
data={
|
2563
|
+
UserCredential.user_credential_hashed_password.name: local_str_hashed_password,
|
2564
|
+
},
|
2565
|
+
)
|
2566
|
+
# mark the backup code as used
|
2567
|
+
global_object_square_database_helper.edit_rows_v0(
|
2568
|
+
database_name=global_string_database_name,
|
2569
|
+
schema_name=global_string_schema_name,
|
2570
|
+
table_name=UserVerificationCode.__tablename__,
|
2571
|
+
filters=FiltersV0(
|
2572
|
+
root={
|
2573
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
2574
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
2575
|
+
eq=VerificationCodeTypeEnum.BACKUP_CODE_RECOVERY.value
|
2576
|
+
),
|
2577
|
+
UserVerificationCode.user_verification_code_hash.name: FilterConditionsV0(
|
2578
|
+
eq=local_list_response_user_verification_code[0][
|
2579
|
+
UserVerificationCode.user_verification_code_hash.name
|
2580
|
+
]
|
2581
|
+
),
|
2582
|
+
}
|
2583
|
+
),
|
2584
|
+
data={
|
2585
|
+
UserVerificationCode.user_verification_code_used_at.name: datetime.now(
|
2586
|
+
timezone.utc
|
2587
|
+
).strftime("%Y-%m-%d %H:%M:%S.%f+00"),
|
2588
|
+
},
|
2589
|
+
)
|
2590
|
+
if logout_other_sessions:
|
2591
|
+
# delete all sessions for user
|
2592
|
+
global_object_square_database_helper.delete_rows_v0(
|
2593
|
+
database_name=global_string_database_name,
|
2594
|
+
schema_name=global_string_schema_name,
|
2595
|
+
table_name=UserSession.__tablename__,
|
2596
|
+
filters=FiltersV0(
|
2597
|
+
root={
|
2598
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
2599
|
+
}
|
2600
|
+
),
|
2601
|
+
)
|
2602
|
+
# generate access token and refresh token
|
2603
|
+
local_dict_access_token_payload = {
|
2604
|
+
"app_id": app_id,
|
2605
|
+
"user_id": user_id,
|
2606
|
+
"exp": datetime.now(timezone.utc)
|
2607
|
+
+ timedelta(minutes=config_int_access_token_valid_minutes),
|
2608
|
+
}
|
2609
|
+
local_str_access_token = jwt.encode(
|
2610
|
+
local_dict_access_token_payload, config_str_secret_key_for_access_token
|
2611
|
+
)
|
2612
|
+
local_object_refresh_token_expiry_time = datetime.now(timezone.utc) + timedelta(
|
2613
|
+
minutes=config_int_refresh_token_valid_minutes
|
2614
|
+
)
|
2615
|
+
local_dict_refresh_token_payload = {
|
2616
|
+
"app_id": app_id,
|
2617
|
+
"user_id": user_id,
|
2618
|
+
"exp": local_object_refresh_token_expiry_time,
|
2619
|
+
}
|
2620
|
+
local_str_refresh_token = jwt.encode(
|
2621
|
+
local_dict_refresh_token_payload, config_str_secret_key_for_refresh_token
|
2622
|
+
)
|
2623
|
+
# insert the refresh token in the database
|
2624
|
+
global_object_square_database_helper.insert_rows_v0(
|
2625
|
+
database_name=global_string_database_name,
|
2626
|
+
schema_name=global_string_schema_name,
|
2627
|
+
table_name=UserSession.__tablename__,
|
2628
|
+
data=[
|
2629
|
+
{
|
2630
|
+
UserSession.user_id.name: user_id,
|
2631
|
+
UserSession.app_id.name: app_id,
|
2632
|
+
UserSession.user_session_refresh_token.name: local_str_refresh_token,
|
2633
|
+
UserSession.user_session_expiry_time.name: local_object_refresh_token_expiry_time.strftime(
|
2634
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
2635
|
+
),
|
2636
|
+
}
|
2637
|
+
],
|
2638
|
+
)
|
2639
|
+
"""
|
2640
|
+
return value
|
2641
|
+
"""
|
2642
|
+
output_content = get_api_output_in_standard_format(
|
2643
|
+
message=messages["GENERIC_ACTION_SUCCESSFUL"],
|
2644
|
+
data={
|
2645
|
+
"main": {
|
2646
|
+
"user_id": user_id,
|
2647
|
+
"access_token": local_str_access_token,
|
2648
|
+
"refresh_token": local_str_refresh_token,
|
2649
|
+
"refresh_token_expiry_time": local_object_refresh_token_expiry_time.isoformat(),
|
2650
|
+
}
|
2651
|
+
},
|
2652
|
+
)
|
2653
|
+
|
2654
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
2655
|
+
except HTTPException as http_exception:
|
2656
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
2657
|
+
return JSONResponse(
|
2658
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
2659
|
+
)
|
2660
|
+
except Exception as e:
|
2661
|
+
"""
|
2662
|
+
rollback logic
|
2663
|
+
"""
|
2664
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
2665
|
+
output_content = get_api_output_in_standard_format(
|
2666
|
+
message=messages["GENERIC_500"],
|
2667
|
+
log=str(e),
|
2668
|
+
)
|
2669
|
+
return JSONResponse(
|
2670
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
2671
|
+
)
|
2672
|
+
|
2673
|
+
|
2674
|
+
@global_object_square_logger.auto_logger()
|
2675
|
+
def util_send_reset_password_email_v0(
|
2676
|
+
username,
|
2677
|
+
):
|
2678
|
+
try:
|
2679
|
+
"""
|
2680
|
+
validation
|
2681
|
+
"""
|
2682
|
+
# validate username
|
2683
|
+
local_list_authentication_user_response = (
|
2684
|
+
global_object_square_database_helper.get_rows_v0(
|
2685
|
+
database_name=global_string_database_name,
|
2686
|
+
schema_name=global_string_schema_name,
|
2687
|
+
table_name=User.__tablename__,
|
2688
|
+
filters=FiltersV0(
|
2689
|
+
root={User.user_username.name: FilterConditionsV0(eq=username)}
|
2690
|
+
),
|
2691
|
+
)["data"]["main"]
|
2692
|
+
)
|
2693
|
+
if len(local_list_authentication_user_response) != 1:
|
2694
|
+
output_content = get_api_output_in_standard_format(
|
2695
|
+
message=messages["INCORRECT_USERNAME"],
|
2696
|
+
log=f"incorrect username: {username}.",
|
2697
|
+
)
|
2698
|
+
raise HTTPException(
|
2699
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2700
|
+
detail=output_content,
|
2701
|
+
)
|
2702
|
+
user_id = local_list_authentication_user_response[0][User.user_id.name]
|
2703
|
+
# check if user has SELF auth provider
|
2704
|
+
local_list_response_user_auth_provider = (
|
2705
|
+
global_object_square_database_helper.get_rows_v0(
|
2706
|
+
database_name=global_string_database_name,
|
2707
|
+
schema_name=global_string_schema_name,
|
2708
|
+
table_name=UserAuthProvider.__tablename__,
|
2709
|
+
filters=FiltersV0(
|
2710
|
+
root={
|
2711
|
+
UserAuthProvider.user_id.name: FilterConditionsV0(eq=user_id),
|
2712
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
2713
|
+
eq=AuthProviderEnum.SELF.value
|
2714
|
+
),
|
2715
|
+
}
|
2716
|
+
),
|
2717
|
+
)["data"]["main"]
|
2718
|
+
)
|
2719
|
+
if len(local_list_response_user_auth_provider) != 1:
|
2720
|
+
output_content = get_api_output_in_standard_format(
|
2721
|
+
message=messages["INCORRECT_AUTH_PROVIDER"],
|
2722
|
+
log=f"user_id: {user_id} does not have {AuthProviderEnum.SELF.value} auth provider.",
|
2723
|
+
)
|
2724
|
+
raise HTTPException(
|
2725
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2726
|
+
detail=output_content,
|
2727
|
+
)
|
2728
|
+
# check if user has recovery method enabled
|
2729
|
+
local_list_response_user_recovery_methods = global_object_square_database_helper.get_rows_v0(
|
2730
|
+
database_name=global_string_database_name,
|
2731
|
+
schema_name=global_string_schema_name,
|
2732
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2733
|
+
filters=FiltersV0(
|
2734
|
+
root={
|
2735
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
2736
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
2737
|
+
eq=RecoveryMethodEnum.EMAIL.value
|
2738
|
+
),
|
2739
|
+
}
|
2740
|
+
),
|
2741
|
+
)[
|
2742
|
+
"data"
|
2743
|
+
][
|
2744
|
+
"main"
|
2745
|
+
]
|
2746
|
+
if len(local_list_response_user_recovery_methods) != 1:
|
2747
|
+
output_content = get_api_output_in_standard_format(
|
2748
|
+
message=messages["RECOVERY_METHOD_NOT_ENABLED"],
|
2749
|
+
log=f"user_id: {user_id} does not have email recovery method enabled.",
|
2750
|
+
)
|
2751
|
+
raise HTTPException(
|
2752
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2753
|
+
detail=output_content,
|
2754
|
+
)
|
2755
|
+
# validate if user has email in profile
|
2756
|
+
user_profile_response = global_object_square_database_helper.get_rows_v0(
|
2757
|
+
database_name=global_string_database_name,
|
2758
|
+
schema_name=global_string_schema_name,
|
2759
|
+
table_name=UserProfile.__tablename__,
|
2760
|
+
filters=FiltersV0(
|
2761
|
+
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
2762
|
+
),
|
2763
|
+
apply_filters=True,
|
2764
|
+
)
|
2765
|
+
user_profile_data = user_profile_response["data"]["main"][0]
|
2766
|
+
if not user_profile_data.get(UserProfile.user_profile_email.name):
|
2767
|
+
output_content = get_api_output_in_standard_format(
|
2768
|
+
message=messages["GENERIC_MISSING_REQUIRED_FIELD"],
|
2769
|
+
log="email is required to send verification email.",
|
2770
|
+
)
|
2771
|
+
raise HTTPException(
|
2772
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2773
|
+
detail=output_content,
|
2774
|
+
)
|
2775
|
+
# check if email is not verified
|
2776
|
+
if not user_profile_data.get(UserProfile.user_profile_email_verified.name):
|
2777
|
+
output_content = get_api_output_in_standard_format(
|
2778
|
+
message=messages["EMAIL_NOT_VERIFIED"],
|
2779
|
+
log="email is not verified.",
|
2780
|
+
)
|
2781
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
2782
|
+
# check if reset password code already exists
|
2783
|
+
local_list_response_user_verification_code = global_object_square_database_helper.get_rows_v0(
|
2784
|
+
database_name=global_string_database_name,
|
2785
|
+
schema_name=global_string_schema_name,
|
2786
|
+
table_name=UserVerificationCode.__tablename__,
|
2787
|
+
filters=FiltersV0(
|
2788
|
+
root={
|
2789
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
2790
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
2791
|
+
eq=VerificationCodeTypeEnum.EMAIL_RECOVERY.value
|
2792
|
+
),
|
2793
|
+
UserVerificationCode.user_verification_code_used_at.name: FilterConditionsV0(
|
2794
|
+
is_null=True
|
2795
|
+
),
|
2796
|
+
UserVerificationCode.user_verification_code_expires_at.name: FilterConditionsV0(
|
2797
|
+
gte=datetime.now(timezone.utc).strftime(
|
2798
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
2799
|
+
)
|
2800
|
+
),
|
2801
|
+
}
|
2802
|
+
),
|
2803
|
+
order_by=[
|
2804
|
+
"-" + UserVerificationCode.user_verification_code_created_at.name
|
2805
|
+
],
|
2806
|
+
limit=1,
|
2807
|
+
apply_filters=True,
|
2808
|
+
)[
|
2809
|
+
"data"
|
2810
|
+
][
|
2811
|
+
"main"
|
2812
|
+
]
|
2813
|
+
if len(local_list_response_user_verification_code) > 0:
|
2814
|
+
existing_verification_code_data = (
|
2815
|
+
local_list_response_user_verification_code[0]
|
2816
|
+
)
|
2817
|
+
if (
|
2818
|
+
datetime.now(timezone.utc)
|
2819
|
+
- datetime.fromisoformat(
|
2820
|
+
existing_verification_code_data[
|
2821
|
+
UserVerificationCode.user_verification_code_created_at.name
|
2822
|
+
]
|
2823
|
+
)
|
2824
|
+
).total_seconds() < RESEND_COOL_DOWN_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS:
|
2825
|
+
output_content = get_api_output_in_standard_format(
|
2826
|
+
message=messages["GENERIC_400"],
|
2827
|
+
log="verification code already exists and was sent within the cooldown period.",
|
2828
|
+
)
|
2829
|
+
raise HTTPException(
|
2830
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2831
|
+
detail=output_content,
|
2832
|
+
)
|
2833
|
+
"""
|
2834
|
+
main process
|
2835
|
+
"""
|
2836
|
+
verification_code = random.randint(
|
2837
|
+
10 ** (NUMBER_OF_DIGITS_IN_EMAIL_PASSWORD_RESET_CODE - 1),
|
2838
|
+
10**NUMBER_OF_DIGITS_IN_EMAIL_PASSWORD_RESET_CODE - 1,
|
2839
|
+
)
|
2840
|
+
# hash the verification code
|
2841
|
+
hashed_verification_code = bcrypt.hashpw(
|
2842
|
+
str(verification_code).encode("utf-8"), bcrypt.gensalt()
|
2843
|
+
).decode("utf-8")
|
2844
|
+
expires_at = datetime.now(timezone.utc) + timedelta(
|
2845
|
+
seconds=EXPIRY_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS
|
2846
|
+
)
|
2847
|
+
# add verification code to UserVerification code table
|
2848
|
+
global_object_square_database_helper.insert_rows_v0(
|
2849
|
+
database_name=global_string_database_name,
|
2850
|
+
schema_name=global_string_schema_name,
|
2851
|
+
table_name=UserVerificationCode.__tablename__,
|
2852
|
+
data=[
|
2853
|
+
{
|
2854
|
+
UserVerificationCode.user_id.name: user_id,
|
2855
|
+
UserVerificationCode.user_verification_code_type.name: VerificationCodeTypeEnum.EMAIL_RECOVERY.value,
|
2856
|
+
UserVerificationCode.user_verification_code_hash.name: hashed_verification_code,
|
2857
|
+
UserVerificationCode.user_verification_code_expires_at.name: expires_at.strftime(
|
2858
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
2859
|
+
),
|
2860
|
+
}
|
2861
|
+
],
|
2862
|
+
)
|
2863
|
+
# send verification email
|
2864
|
+
if (
|
2865
|
+
user_profile_data[UserProfile.user_profile_first_name.name]
|
2866
|
+
and user_profile_data[UserProfile.user_profile_last_name.name]
|
2867
|
+
):
|
2868
|
+
user_to_name = f"{user_profile_data[UserProfile.user_profile_first_name.name]} {user_profile_data[UserProfile.user_profile_last_name.name]}"
|
2869
|
+
elif user_profile_data[UserProfile.user_profile_first_name.name]:
|
2870
|
+
user_to_name = user_profile_data[UserProfile.user_profile_first_name.name]
|
2871
|
+
elif user_profile_data[UserProfile.user_profile_last_name.name]:
|
2872
|
+
user_to_name = user_profile_data[UserProfile.user_profile_last_name.name]
|
2873
|
+
else:
|
2874
|
+
user_to_name = ""
|
2875
|
+
|
2876
|
+
mailgun_response = send_email_using_mailgun(
|
2877
|
+
from_email="auth@thepmsquare.com",
|
2878
|
+
from_name="square_authentication",
|
2879
|
+
to_email=user_profile_data[UserProfile.user_profile_email.name],
|
2880
|
+
to_name=user_to_name,
|
2881
|
+
subject="Password Reset Verification Code",
|
2882
|
+
body=f"Your Password Reset verification code is {verification_code}. It will expire in {EXPIRY_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS / 60} minutes.",
|
2883
|
+
api_key=MAIL_GUN_API_KEY,
|
2884
|
+
domain_name="thepmsquare.com",
|
2885
|
+
)
|
2886
|
+
# add log for email sending
|
2887
|
+
global_object_square_database_helper.insert_rows_v0(
|
2888
|
+
database_name=global_string_database_name,
|
2889
|
+
schema_name=email_schema_name,
|
2890
|
+
table_name=EmailLog.__tablename__,
|
2891
|
+
data=[
|
2892
|
+
{
|
2893
|
+
EmailLog.user_id.name: user_id,
|
2894
|
+
EmailLog.recipient_email.name: user_profile_data[
|
2895
|
+
UserProfile.user_profile_email.name
|
2896
|
+
],
|
2897
|
+
EmailLog.email_type.name: EmailTypeEnum.VERIFY_EMAIL.value,
|
2898
|
+
EmailLog.status.name: EmailStatusEnum.SENT.value,
|
2899
|
+
EmailLog.third_party_message_id.name: mailgun_response.get("id"),
|
2900
|
+
}
|
2901
|
+
],
|
2902
|
+
)
|
2903
|
+
"""
|
2904
|
+
return value
|
2905
|
+
"""
|
2906
|
+
cooldown_reset_at = datetime.now(timezone.utc) + timedelta(
|
2907
|
+
seconds=EXPIRY_TIME_FOR_EMAIL_PASSWORD_RESET_CODE_IN_SECONDS,
|
2908
|
+
)
|
2909
|
+
|
2910
|
+
output_content = get_api_output_in_standard_format(
|
2911
|
+
data={
|
2912
|
+
"expires_at": expires_at.isoformat(),
|
2913
|
+
"cooldown_reset_at": cooldown_reset_at.isoformat(),
|
2914
|
+
},
|
2915
|
+
message=messages["GENERIC_ACTION_SUCCESSFUL"],
|
2916
|
+
)
|
2917
|
+
return JSONResponse(
|
2918
|
+
status_code=status.HTTP_200_OK,
|
2919
|
+
content=output_content,
|
2920
|
+
)
|
2921
|
+
except HTTPException as http_exception:
|
2922
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
2923
|
+
return JSONResponse(
|
2924
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
2925
|
+
)
|
2926
|
+
except Exception as e:
|
2927
|
+
"""
|
2928
|
+
rollback logic
|
2929
|
+
"""
|
2930
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
2931
|
+
output_content = get_api_output_in_standard_format(
|
2932
|
+
message=messages["GENERIC_500"],
|
2933
|
+
log=str(e),
|
2934
|
+
)
|
2935
|
+
return JSONResponse(
|
2936
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
2937
|
+
)
|
2938
|
+
|
2939
|
+
|
2940
|
+
@global_object_square_logger.auto_logger()
|
2941
|
+
def util_reset_password_and_login_using_reset_email_code_v0(
|
2942
|
+
reset_email_code, username, new_password, app_id, logout_other_sessions
|
2943
|
+
):
|
2944
|
+
try:
|
2945
|
+
"""
|
2946
|
+
validation
|
2947
|
+
"""
|
2948
|
+
# validate username
|
2949
|
+
local_list_authentication_user_response = (
|
2950
|
+
global_object_square_database_helper.get_rows_v0(
|
2951
|
+
database_name=global_string_database_name,
|
2952
|
+
schema_name=global_string_schema_name,
|
2953
|
+
table_name=User.__tablename__,
|
2954
|
+
filters=FiltersV0(
|
2955
|
+
root={User.user_username.name: FilterConditionsV0(eq=username)}
|
2956
|
+
),
|
2957
|
+
)["data"]["main"]
|
2958
|
+
)
|
2959
|
+
if len(local_list_authentication_user_response) != 1:
|
2960
|
+
output_content = get_api_output_in_standard_format(
|
2961
|
+
message=messages["INCORRECT_USERNAME"],
|
2962
|
+
log=f"incorrect username: {username}.",
|
2963
|
+
)
|
2964
|
+
raise HTTPException(
|
2965
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2966
|
+
detail=output_content,
|
2967
|
+
)
|
2968
|
+
user_id = local_list_authentication_user_response[0][User.user_id.name]
|
2969
|
+
# check if user has SELF auth provider
|
2970
|
+
local_list_response_user_auth_provider = (
|
2971
|
+
global_object_square_database_helper.get_rows_v0(
|
2972
|
+
database_name=global_string_database_name,
|
2973
|
+
schema_name=global_string_schema_name,
|
2974
|
+
table_name=UserAuthProvider.__tablename__,
|
2975
|
+
filters=FiltersV0(
|
2976
|
+
root={
|
2977
|
+
UserAuthProvider.user_id.name: FilterConditionsV0(eq=user_id),
|
2978
|
+
UserAuthProvider.auth_provider.name: FilterConditionsV0(
|
2979
|
+
eq=AuthProviderEnum.SELF.value
|
2980
|
+
),
|
2981
|
+
}
|
2982
|
+
),
|
2983
|
+
)["data"]["main"]
|
2984
|
+
)
|
2985
|
+
if len(local_list_response_user_auth_provider) != 1:
|
2986
|
+
output_content = get_api_output_in_standard_format(
|
2987
|
+
message=messages["INCORRECT_AUTH_PROVIDER"],
|
2988
|
+
log=f"user_id: {user_id} does not have {AuthProviderEnum.SELF.value} auth provider.",
|
2989
|
+
)
|
2990
|
+
raise HTTPException(
|
2991
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
2992
|
+
detail=output_content,
|
2993
|
+
)
|
2994
|
+
# check if user has recovery method enabled
|
2995
|
+
local_list_response_user_recovery_methods = global_object_square_database_helper.get_rows_v0(
|
2996
|
+
database_name=global_string_database_name,
|
2997
|
+
schema_name=global_string_schema_name,
|
2998
|
+
table_name=UserRecoveryMethod.__tablename__,
|
2999
|
+
filters=FiltersV0(
|
3000
|
+
root={
|
3001
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
3002
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
3003
|
+
eq=RecoveryMethodEnum.EMAIL.value
|
3004
|
+
),
|
3005
|
+
}
|
3006
|
+
),
|
3007
|
+
)[
|
3008
|
+
"data"
|
3009
|
+
][
|
3010
|
+
"main"
|
3011
|
+
]
|
3012
|
+
if len(local_list_response_user_recovery_methods) != 1:
|
3013
|
+
output_content = get_api_output_in_standard_format(
|
3014
|
+
message=messages["RECOVERY_METHOD_NOT_ENABLED"],
|
3015
|
+
log=f"user_id: {user_id} does not have email recovery method enabled.",
|
3016
|
+
)
|
3017
|
+
raise HTTPException(
|
3018
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
3019
|
+
detail=output_content,
|
3020
|
+
)
|
3021
|
+
# check if user has email in profile
|
3022
|
+
user_profile_response = global_object_square_database_helper.get_rows_v0(
|
3023
|
+
database_name=global_string_database_name,
|
3024
|
+
schema_name=global_string_schema_name,
|
3025
|
+
table_name=UserProfile.__tablename__,
|
3026
|
+
filters=FiltersV0(
|
3027
|
+
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
3028
|
+
),
|
3029
|
+
apply_filters=True,
|
3030
|
+
)
|
3031
|
+
user_profile_data = user_profile_response["data"]["main"][0]
|
3032
|
+
if not user_profile_data.get(UserProfile.user_profile_email.name):
|
3033
|
+
output_content = get_api_output_in_standard_format(
|
3034
|
+
message=messages["GENERIC_MISSING_REQUIRED_FIELD"],
|
3035
|
+
log="user seems to have email recovery method enabled, but does not have email in profile.",
|
3036
|
+
)
|
3037
|
+
raise HTTPException(
|
3038
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
3039
|
+
detail=output_content,
|
3040
|
+
)
|
3041
|
+
# check if email is verified.
|
3042
|
+
if not user_profile_data.get(UserProfile.user_profile_email_verified.name):
|
3043
|
+
output_content = get_api_output_in_standard_format(
|
3044
|
+
message=messages["EMAIL_NOT_VERIFIED"],
|
3045
|
+
log="email is not verified.",
|
3046
|
+
)
|
3047
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
3048
|
+
# validate if user is assigned to the app.
|
3049
|
+
# not checking [skipping] if the app exists, as it is not required for this endpoint.
|
3050
|
+
local_list_response_user_app = global_object_square_database_helper.get_rows_v0(
|
3051
|
+
database_name=global_string_database_name,
|
3052
|
+
schema_name=global_string_schema_name,
|
3053
|
+
table_name=UserApp.__tablename__,
|
3054
|
+
filters=FiltersV0(
|
3055
|
+
root={
|
3056
|
+
UserApp.user_id.name: FilterConditionsV0(eq=user_id),
|
3057
|
+
UserApp.app_id.name: FilterConditionsV0(eq=app_id),
|
3058
|
+
}
|
3059
|
+
),
|
3060
|
+
)["data"]["main"]
|
3061
|
+
if len(local_list_response_user_app) == 0:
|
3062
|
+
output_content = get_api_output_in_standard_format(
|
3063
|
+
message=messages["GENERIC_400"],
|
3064
|
+
log=f"user_id: {user_id} is not assigned to app_id: {app_id}.",
|
3065
|
+
)
|
3066
|
+
raise HTTPException(
|
3067
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
3068
|
+
detail=output_content,
|
3069
|
+
)
|
3070
|
+
"""
|
3071
|
+
main process
|
3072
|
+
"""
|
3073
|
+
# validate email reset code
|
3074
|
+
local_list_response_user_verification_code = global_object_square_database_helper.get_rows_v0(
|
3075
|
+
database_name=global_string_database_name,
|
3076
|
+
schema_name=global_string_schema_name,
|
3077
|
+
table_name=UserVerificationCode.__tablename__,
|
3078
|
+
filters=FiltersV0(
|
3079
|
+
root={
|
3080
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
3081
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
3082
|
+
eq=VerificationCodeTypeEnum.EMAIL_RECOVERY.value
|
3083
|
+
),
|
3084
|
+
UserVerificationCode.user_verification_code_expires_at.name: FilterConditionsV0(
|
3085
|
+
gte=datetime.now(timezone.utc).strftime(
|
3086
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
3087
|
+
)
|
3088
|
+
),
|
3089
|
+
UserVerificationCode.user_verification_code_used_at.name: FilterConditionsV0(
|
3090
|
+
is_null=True
|
3091
|
+
),
|
3092
|
+
}
|
3093
|
+
),
|
3094
|
+
columns=[UserVerificationCode.user_verification_code_hash.name],
|
3095
|
+
order_by=[
|
3096
|
+
"-" + UserVerificationCode.user_verification_code_created_at.name
|
3097
|
+
],
|
3098
|
+
limit=1,
|
3099
|
+
)[
|
3100
|
+
"data"
|
3101
|
+
][
|
3102
|
+
"main"
|
3103
|
+
]
|
3104
|
+
if len(local_list_response_user_verification_code) != 1:
|
3105
|
+
output_content = get_api_output_in_standard_format(
|
3106
|
+
message=messages["INCORRECT_VERIFICATION_CODE"],
|
3107
|
+
log=f"incorrect reset_email_code: {reset_email_code} for user_id: {user_id}.",
|
3108
|
+
)
|
3109
|
+
raise HTTPException(
|
3110
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
3111
|
+
detail=output_content,
|
3112
|
+
)
|
3113
|
+
latest_hashed_verification_code = local_list_response_user_verification_code[0][
|
3114
|
+
UserVerificationCode.user_verification_code_hash.name
|
3115
|
+
]
|
3116
|
+
|
3117
|
+
if not bcrypt.checkpw(
|
3118
|
+
reset_email_code.encode("utf-8"),
|
3119
|
+
latest_hashed_verification_code.encode("utf-8"),
|
3120
|
+
):
|
3121
|
+
output_content = get_api_output_in_standard_format(
|
3122
|
+
message=messages["INCORRECT_VERIFICATION_CODE"],
|
3123
|
+
log=f"incorrect reset_email_code: {reset_email_code} for user_id: {user_id}.",
|
3124
|
+
)
|
3125
|
+
raise HTTPException(
|
3126
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
3127
|
+
detail=output_content,
|
3128
|
+
)
|
3129
|
+
|
3130
|
+
# hash the new password
|
3131
|
+
local_str_hashed_password = bcrypt.hashpw(
|
3132
|
+
new_password.encode("utf-8"), bcrypt.gensalt()
|
3133
|
+
).decode("utf-8")
|
3134
|
+
# update the password
|
3135
|
+
global_object_square_database_helper.edit_rows_v0(
|
3136
|
+
database_name=global_string_database_name,
|
3137
|
+
schema_name=global_string_schema_name,
|
3138
|
+
table_name=UserCredential.__tablename__,
|
3139
|
+
filters=FiltersV0(
|
3140
|
+
root={
|
3141
|
+
UserCredential.user_id.name: FilterConditionsV0(eq=user_id),
|
3142
|
+
}
|
3143
|
+
),
|
3144
|
+
data={
|
3145
|
+
UserCredential.user_credential_hashed_password.name: local_str_hashed_password,
|
3146
|
+
},
|
3147
|
+
)
|
3148
|
+
# mark the email code as used
|
3149
|
+
global_object_square_database_helper.edit_rows_v0(
|
3150
|
+
database_name=global_string_database_name,
|
3151
|
+
schema_name=global_string_schema_name,
|
3152
|
+
table_name=UserVerificationCode.__tablename__,
|
3153
|
+
filters=FiltersV0(
|
3154
|
+
root={
|
3155
|
+
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
3156
|
+
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
3157
|
+
eq=VerificationCodeTypeEnum.EMAIL_RECOVERY.value
|
3158
|
+
),
|
3159
|
+
UserVerificationCode.user_verification_code_hash.name: FilterConditionsV0(
|
3160
|
+
eq=latest_hashed_verification_code
|
3161
|
+
),
|
3162
|
+
}
|
3163
|
+
),
|
3164
|
+
data={
|
3165
|
+
UserVerificationCode.user_verification_code_used_at.name: datetime.now(
|
3166
|
+
timezone.utc
|
3167
|
+
).strftime("%Y-%m-%d %H:%M:%S.%f+00"),
|
3168
|
+
},
|
3169
|
+
)
|
3170
|
+
if logout_other_sessions:
|
3171
|
+
# delete all sessions for user
|
3172
|
+
global_object_square_database_helper.delete_rows_v0(
|
3173
|
+
database_name=global_string_database_name,
|
3174
|
+
schema_name=global_string_schema_name,
|
3175
|
+
table_name=UserSession.__tablename__,
|
3176
|
+
filters=FiltersV0(
|
3177
|
+
root={
|
3178
|
+
UserSession.user_id.name: FilterConditionsV0(eq=user_id),
|
3179
|
+
}
|
3180
|
+
),
|
3181
|
+
)
|
3182
|
+
# generate access token and refresh token
|
3183
|
+
local_dict_access_token_payload = {
|
3184
|
+
"app_id": app_id,
|
3185
|
+
"user_id": user_id,
|
3186
|
+
"exp": datetime.now(timezone.utc)
|
3187
|
+
+ timedelta(minutes=config_int_access_token_valid_minutes),
|
3188
|
+
}
|
3189
|
+
local_str_access_token = jwt.encode(
|
3190
|
+
local_dict_access_token_payload, config_str_secret_key_for_access_token
|
3191
|
+
)
|
3192
|
+
local_object_refresh_token_expiry_time = datetime.now(timezone.utc) + timedelta(
|
3193
|
+
minutes=config_int_refresh_token_valid_minutes
|
3194
|
+
)
|
3195
|
+
local_dict_refresh_token_payload = {
|
3196
|
+
"app_id": app_id,
|
3197
|
+
"user_id": user_id,
|
3198
|
+
"exp": local_object_refresh_token_expiry_time,
|
3199
|
+
}
|
3200
|
+
local_str_refresh_token = jwt.encode(
|
3201
|
+
local_dict_refresh_token_payload, config_str_secret_key_for_refresh_token
|
3202
|
+
)
|
3203
|
+
# insert the refresh token in the database
|
3204
|
+
global_object_square_database_helper.insert_rows_v0(
|
3205
|
+
database_name=global_string_database_name,
|
3206
|
+
schema_name=global_string_schema_name,
|
3207
|
+
table_name=UserSession.__tablename__,
|
3208
|
+
data=[
|
3209
|
+
{
|
3210
|
+
UserSession.user_id.name: user_id,
|
3211
|
+
UserSession.app_id.name: app_id,
|
3212
|
+
UserSession.user_session_refresh_token.name: local_str_refresh_token,
|
3213
|
+
UserSession.user_session_expiry_time.name: local_object_refresh_token_expiry_time.strftime(
|
3214
|
+
"%Y-%m-%d %H:%M:%S.%f+00"
|
3215
|
+
),
|
3216
|
+
}
|
3217
|
+
],
|
3218
|
+
)
|
3219
|
+
"""
|
3220
|
+
return value
|
3221
|
+
"""
|
3222
|
+
output_content = get_api_output_in_standard_format(
|
3223
|
+
message=messages["GENERIC_ACTION_SUCCESSFUL"],
|
3224
|
+
data={
|
3225
|
+
"main": {
|
3226
|
+
"user_id": user_id,
|
3227
|
+
"access_token": local_str_access_token,
|
3228
|
+
"refresh_token": local_str_refresh_token,
|
3229
|
+
"refresh_token_expiry_time": local_object_refresh_token_expiry_time.isoformat(),
|
3230
|
+
}
|
3231
|
+
},
|
3232
|
+
)
|
3233
|
+
|
3234
|
+
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
3235
|
+
except HTTPException as http_exception:
|
3236
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
3237
|
+
return JSONResponse(
|
3238
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
3239
|
+
)
|
3240
|
+
except Exception as e:
|
3241
|
+
"""
|
3242
|
+
rollback logic
|
3243
|
+
"""
|
3244
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
3245
|
+
output_content = get_api_output_in_standard_format(
|
3246
|
+
message=messages["GENERIC_500"],
|
3247
|
+
log=str(e),
|
3248
|
+
)
|
3249
|
+
return JSONResponse(
|
3250
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
3251
|
+
)
|