square-authentication 10.0.1__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 -3013
- 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.1.dist-info → square_authentication-10.0.2.dist-info}/METADATA +7 -2
- {square_authentication-10.0.1.dist-info → square_authentication-10.0.2.dist-info}/RECORD +12 -8
- {square_authentication-10.0.1.dist-info → square_authentication-10.0.2.dist-info}/WHEEL +0 -0
- {square_authentication-10.0.1.dist-info → square_authentication-10.0.2.dist-info}/licenses/LICENSE +0 -0
- {square_authentication-10.0.1.dist-info → square_authentication-10.0.2.dist-info}/top_level.txt +0 -0
@@ -1,44 +1,22 @@
|
|
1
|
-
import random
|
2
|
-
from datetime import datetime, timedelta, timezone
|
3
1
|
from typing import Annotated, Optional
|
4
2
|
|
5
|
-
import bcrypt
|
6
3
|
from fastapi import APIRouter, Header, HTTPException, UploadFile, status
|
7
4
|
from fastapi.responses import JSONResponse
|
8
5
|
from square_commons import get_api_output_in_standard_format
|
9
|
-
from square_commons.email import send_email_using_mailgun
|
10
|
-
from square_database_helper import FiltersV0
|
11
|
-
from square_database_helper.pydantic_models import FilterConditionsV0
|
12
|
-
from square_database_structure.square import global_string_database_name
|
13
|
-
from square_database_structure.square.authentication import global_string_schema_name
|
14
|
-
from square_database_structure.square.authentication.enums import (
|
15
|
-
VerificationCodeTypeEnum,
|
16
|
-
)
|
17
|
-
from square_database_structure.square.authentication.tables import (
|
18
|
-
UserProfile,
|
19
|
-
UserVerificationCode,
|
20
|
-
)
|
21
|
-
from square_database_structure.square.email import (
|
22
|
-
global_string_schema_name as email_schema_name,
|
23
|
-
)
|
24
|
-
from square_database_structure.square.email.enums import EmailStatusEnum, EmailTypeEnum
|
25
|
-
from square_database_structure.square.email.tables import EmailLog
|
26
6
|
|
27
7
|
from square_authentication.configuration import (
|
28
|
-
EXPIRY_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS,
|
29
|
-
MAIL_GUN_API_KEY,
|
30
|
-
NUMBER_OF_DIGITS_IN_EMAIL_VERIFICATION_CODE,
|
31
|
-
RESEND_COOL_DOWN_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS,
|
32
|
-
config_str_secret_key_for_access_token,
|
33
|
-
global_object_square_database_helper,
|
34
|
-
global_object_square_file_store_helper,
|
35
8
|
global_object_square_logger,
|
36
9
|
)
|
37
10
|
from square_authentication.messages import messages
|
38
11
|
from square_authentication.pydantic_models.profile import (
|
39
12
|
ValidateEmailVerificationCodeV0,
|
40
13
|
)
|
41
|
-
from square_authentication.utils.
|
14
|
+
from square_authentication.utils.routes.profile import (
|
15
|
+
util_update_profile_photo_v0,
|
16
|
+
util_update_profile_details_v0,
|
17
|
+
util_send_verification_email_v0,
|
18
|
+
util_validate_email_verification_code_v0,
|
19
|
+
)
|
42
20
|
|
43
21
|
router = APIRouter(
|
44
22
|
tags=["profile"],
|
@@ -51,137 +29,18 @@ async def update_profile_photo_v0(
|
|
51
29
|
access_token: Annotated[str, Header()],
|
52
30
|
profile_photo: Optional[UploadFile] = None,
|
53
31
|
):
|
54
|
-
|
55
32
|
try:
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# validate access token
|
60
|
-
try:
|
61
|
-
local_dict_access_token_payload = get_jwt_payload(
|
62
|
-
access_token, config_str_secret_key_for_access_token
|
63
|
-
)
|
64
|
-
except Exception as error:
|
65
|
-
output_content = get_api_output_in_standard_format(
|
66
|
-
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
67
|
-
)
|
68
|
-
raise HTTPException(
|
69
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
70
|
-
detail=output_content,
|
71
|
-
)
|
72
|
-
user_id = local_dict_access_token_payload["user_id"]
|
73
|
-
|
74
|
-
# validate file format
|
75
|
-
if profile_photo and not profile_photo.filename.endswith(
|
76
|
-
(".jpg", ".jpeg", ".png")
|
77
|
-
):
|
78
|
-
output_content = get_api_output_in_standard_format(
|
79
|
-
message=messages["INVALID_FILE_FORMAT"]
|
80
|
-
)
|
81
|
-
raise HTTPException(
|
82
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
83
|
-
detail=output_content,
|
84
|
-
)
|
85
|
-
|
86
|
-
# validate file size
|
87
|
-
file_size_limit_in_mib = 5
|
88
|
-
if profile_photo and profile_photo.size > (
|
89
|
-
file_size_limit_in_mib * 1024 * 1024
|
90
|
-
):
|
91
|
-
output_content = get_api_output_in_standard_format(
|
92
|
-
message=messages["FILE_SIZE_EXCEEDS_LIMIT"]
|
93
|
-
)
|
94
|
-
raise HTTPException(
|
95
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
96
|
-
detail=output_content,
|
97
|
-
)
|
98
|
-
"""
|
99
|
-
main process
|
100
|
-
"""
|
101
|
-
old_profile_photo_response = global_object_square_database_helper.get_rows_v0(
|
102
|
-
database_name=global_string_database_name,
|
103
|
-
schema_name=global_string_schema_name,
|
104
|
-
table_name=UserProfile.__tablename__,
|
105
|
-
filters=FiltersV0(
|
106
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
107
|
-
),
|
108
|
-
apply_filters=True,
|
109
|
-
)
|
110
|
-
old_profile_photo_token = old_profile_photo_response["data"]["main"][0][
|
111
|
-
"user_profile_photo_storage_token"
|
112
|
-
]
|
113
|
-
|
114
|
-
if profile_photo:
|
115
|
-
# uploading to square file store
|
116
|
-
file_upload_response = (
|
117
|
-
global_object_square_file_store_helper.upload_file_using_tuple_v0(
|
118
|
-
file=(
|
119
|
-
profile_photo.filename,
|
120
|
-
profile_photo.file,
|
121
|
-
profile_photo.content_type,
|
122
|
-
),
|
123
|
-
system_relative_path="global/users/profile_photos",
|
124
|
-
)
|
125
|
-
)
|
126
|
-
# updating user profile
|
127
|
-
profile_update_response = global_object_square_database_helper.edit_rows_v0(
|
128
|
-
data={
|
129
|
-
UserProfile.user_profile_photo_storage_token.name: file_upload_response[
|
130
|
-
"data"
|
131
|
-
][
|
132
|
-
"main"
|
133
|
-
]
|
134
|
-
},
|
135
|
-
filters=FiltersV0(
|
136
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
137
|
-
),
|
138
|
-
database_name=global_string_database_name,
|
139
|
-
schema_name=global_string_schema_name,
|
140
|
-
table_name=UserProfile.__tablename__,
|
141
|
-
apply_filters=True,
|
142
|
-
)
|
143
|
-
else:
|
144
|
-
# updating user profile
|
145
|
-
profile_update_response = global_object_square_database_helper.edit_rows_v0(
|
146
|
-
data={UserProfile.user_profile_photo_storage_token.name: None},
|
147
|
-
filters=FiltersV0(
|
148
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
149
|
-
),
|
150
|
-
database_name=global_string_database_name,
|
151
|
-
schema_name=global_string_schema_name,
|
152
|
-
table_name=UserProfile.__tablename__,
|
153
|
-
apply_filters=True,
|
154
|
-
)
|
155
|
-
|
156
|
-
if old_profile_photo_token:
|
157
|
-
global_object_square_file_store_helper.delete_file_v0(
|
158
|
-
[old_profile_photo_token]
|
159
|
-
)
|
160
|
-
|
161
|
-
"""
|
162
|
-
return value
|
163
|
-
"""
|
164
|
-
output_content = get_api_output_in_standard_format(
|
165
|
-
data=profile_update_response["data"],
|
166
|
-
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
167
|
-
)
|
168
|
-
return JSONResponse(
|
169
|
-
status_code=status.HTTP_200_OK,
|
170
|
-
content=output_content,
|
171
|
-
)
|
172
|
-
except HTTPException as http_exception:
|
173
|
-
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
174
|
-
return JSONResponse(
|
175
|
-
status_code=http_exception.status_code, content=http_exception.detail
|
33
|
+
return util_update_profile_photo_v0(
|
34
|
+
access_token=access_token,
|
35
|
+
profile_photo=profile_photo,
|
176
36
|
)
|
37
|
+
except HTTPException as he:
|
38
|
+
global_object_square_logger.logger.error(he, exc_info=True)
|
39
|
+
return JSONResponse(status_code=he.status_code, content=he.detail)
|
177
40
|
except Exception as e:
|
178
|
-
"""
|
179
|
-
rollback logic
|
180
|
-
"""
|
181
41
|
global_object_square_logger.logger.error(e, exc_info=True)
|
182
42
|
output_content = get_api_output_in_standard_format(
|
183
|
-
message=messages["GENERIC_500"],
|
184
|
-
log=str(e),
|
43
|
+
message=messages["GENERIC_500"], log=str(e)
|
185
44
|
)
|
186
45
|
return JSONResponse(
|
187
46
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
@@ -199,109 +58,21 @@ async def update_profile_details_v0(
|
|
199
58
|
phone_number: Optional[str] = None,
|
200
59
|
):
|
201
60
|
try:
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
)
|
214
|
-
raise HTTPException(
|
215
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
216
|
-
detail=output_content,
|
217
|
-
)
|
218
|
-
user_id = local_dict_access_token_payload["user_id"]
|
219
|
-
|
220
|
-
# validate email format
|
221
|
-
if email and "@" not in email:
|
222
|
-
output_content = get_api_output_in_standard_format(
|
223
|
-
message=messages["INVALID_EMAIL_FORMAT"]
|
224
|
-
)
|
225
|
-
raise HTTPException(
|
226
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
227
|
-
detail=output_content,
|
228
|
-
)
|
229
|
-
|
230
|
-
# validate phone number format
|
231
|
-
if phone_number and not phone_number.isdigit():
|
232
|
-
output_content = get_api_output_in_standard_format(
|
233
|
-
message=messages["INVALID_PHONE_NUMBER_FORMAT"]
|
234
|
-
)
|
235
|
-
raise HTTPException(
|
236
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
237
|
-
detail=output_content,
|
238
|
-
)
|
239
|
-
if (phone_number and not phone_number_country_code) or (
|
240
|
-
phone_number_country_code and not phone_number
|
241
|
-
):
|
242
|
-
output_content = get_api_output_in_standard_format(
|
243
|
-
message=messages["GENERIC_MISSING_REQUIRED_FIELD"],
|
244
|
-
log="both phone number and country code must be provided together.",
|
245
|
-
)
|
246
|
-
raise HTTPException(
|
247
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
248
|
-
detail=output_content,
|
249
|
-
)
|
250
|
-
|
251
|
-
"""
|
252
|
-
main process
|
253
|
-
"""
|
254
|
-
profile_update_data = {}
|
255
|
-
if first_name is not None:
|
256
|
-
profile_update_data[UserProfile.user_profile_first_name.name] = first_name
|
257
|
-
if last_name is not None:
|
258
|
-
profile_update_data[UserProfile.user_profile_last_name.name] = last_name
|
259
|
-
if email is not None:
|
260
|
-
profile_update_data[UserProfile.user_profile_email.name] = email
|
261
|
-
if phone_number is not None and phone_number_country_code is not None:
|
262
|
-
profile_update_data[UserProfile.user_profile_phone_number.name] = (
|
263
|
-
phone_number
|
264
|
-
)
|
265
|
-
profile_update_data[
|
266
|
-
UserProfile.user_profile_phone_number_country_code.name
|
267
|
-
] = phone_number_country_code
|
268
|
-
|
269
|
-
# updating user profile
|
270
|
-
profile_update_response = global_object_square_database_helper.edit_rows_v0(
|
271
|
-
data=profile_update_data,
|
272
|
-
filters=FiltersV0(
|
273
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
274
|
-
),
|
275
|
-
database_name=global_string_database_name,
|
276
|
-
schema_name=global_string_schema_name,
|
277
|
-
table_name=UserProfile.__tablename__,
|
278
|
-
apply_filters=True,
|
279
|
-
)
|
280
|
-
|
281
|
-
"""
|
282
|
-
return value
|
283
|
-
"""
|
284
|
-
output_content = get_api_output_in_standard_format(
|
285
|
-
data=profile_update_response["data"],
|
286
|
-
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
287
|
-
)
|
288
|
-
return JSONResponse(
|
289
|
-
status_code=status.HTTP_200_OK,
|
290
|
-
content=output_content,
|
291
|
-
)
|
292
|
-
except HTTPException as http_exception:
|
293
|
-
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
294
|
-
return JSONResponse(
|
295
|
-
status_code=http_exception.status_code, content=http_exception.detail
|
296
|
-
)
|
61
|
+
return util_update_profile_details_v0(
|
62
|
+
access_token=access_token,
|
63
|
+
first_name=first_name,
|
64
|
+
last_name=last_name,
|
65
|
+
email=email,
|
66
|
+
phone_number_country_code=phone_number_country_code,
|
67
|
+
phone_number=phone_number,
|
68
|
+
)
|
69
|
+
except HTTPException as he:
|
70
|
+
global_object_square_logger.logger.error(he, exc_info=True)
|
71
|
+
return JSONResponse(status_code=he.status_code, content=he.detail)
|
297
72
|
except Exception as e:
|
298
|
-
"""
|
299
|
-
rollback logic
|
300
|
-
"""
|
301
73
|
global_object_square_logger.logger.error(e, exc_info=True)
|
302
74
|
output_content = get_api_output_in_standard_format(
|
303
|
-
message=messages["GENERIC_500"],
|
304
|
-
log=str(e),
|
75
|
+
message=messages["GENERIC_500"], log=str(e)
|
305
76
|
)
|
306
77
|
return JSONResponse(
|
307
78
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
@@ -314,198 +85,16 @@ async def send_verification_email_v0(
|
|
314
85
|
access_token: Annotated[str, Header()],
|
315
86
|
):
|
316
87
|
try:
|
317
|
-
|
318
|
-
|
319
|
-
"""
|
320
|
-
# validate access token
|
321
|
-
try:
|
322
|
-
local_dict_access_token_payload = get_jwt_payload(
|
323
|
-
access_token, config_str_secret_key_for_access_token
|
324
|
-
)
|
325
|
-
except Exception as error:
|
326
|
-
output_content = get_api_output_in_standard_format(
|
327
|
-
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
328
|
-
)
|
329
|
-
raise HTTPException(
|
330
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
331
|
-
detail=output_content,
|
332
|
-
)
|
333
|
-
user_id = local_dict_access_token_payload["user_id"]
|
334
|
-
|
335
|
-
# validate if user has email in profile
|
336
|
-
user_profile_response = global_object_square_database_helper.get_rows_v0(
|
337
|
-
database_name=global_string_database_name,
|
338
|
-
schema_name=global_string_schema_name,
|
339
|
-
table_name=UserProfile.__tablename__,
|
340
|
-
filters=FiltersV0(
|
341
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
342
|
-
),
|
343
|
-
apply_filters=True,
|
344
|
-
)
|
345
|
-
user_profile_data = user_profile_response["data"]["main"][0]
|
346
|
-
if not user_profile_data.get(UserProfile.user_profile_email.name):
|
347
|
-
output_content = get_api_output_in_standard_format(
|
348
|
-
message=messages["GENERIC_MISSING_REQUIRED_FIELD"],
|
349
|
-
log="email is required to send verification email.",
|
350
|
-
)
|
351
|
-
raise HTTPException(
|
352
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
353
|
-
detail=output_content,
|
354
|
-
)
|
355
|
-
# check if email is already verified
|
356
|
-
if user_profile_data.get(UserProfile.user_profile_email_verified.name):
|
357
|
-
output_content = get_api_output_in_standard_format(
|
358
|
-
message=messages["EMAIL_ALREADY_VERIFIED"]
|
359
|
-
)
|
360
|
-
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
361
|
-
# check if email verification code already exists
|
362
|
-
existing_verification_code_response = global_object_square_database_helper.get_rows_v0(
|
363
|
-
database_name=global_string_database_name,
|
364
|
-
schema_name=global_string_schema_name,
|
365
|
-
table_name=UserVerificationCode.__tablename__,
|
366
|
-
filters=FiltersV0(
|
367
|
-
root={
|
368
|
-
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
369
|
-
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
370
|
-
eq=VerificationCodeTypeEnum.EMAIL_VERIFICATION.value
|
371
|
-
),
|
372
|
-
UserVerificationCode.user_verification_code_used_at.name: FilterConditionsV0(
|
373
|
-
is_null=True
|
374
|
-
),
|
375
|
-
UserVerificationCode.user_verification_code_expires_at.name: FilterConditionsV0(
|
376
|
-
gte=datetime.now(timezone.utc).strftime(
|
377
|
-
"%Y-%m-%d %H:%M:%S.%f+00"
|
378
|
-
)
|
379
|
-
),
|
380
|
-
}
|
381
|
-
),
|
382
|
-
order_by=[
|
383
|
-
"-" + UserVerificationCode.user_verification_code_created_at.name
|
384
|
-
],
|
385
|
-
limit=1,
|
386
|
-
apply_filters=True,
|
387
|
-
)
|
388
|
-
if len(existing_verification_code_response["data"]["main"]) > 0:
|
389
|
-
existing_verification_code_data = existing_verification_code_response[
|
390
|
-
"data"
|
391
|
-
]["main"][0]
|
392
|
-
if (
|
393
|
-
datetime.now(timezone.utc)
|
394
|
-
- datetime.fromisoformat(
|
395
|
-
existing_verification_code_data[
|
396
|
-
UserVerificationCode.user_verification_code_created_at.name
|
397
|
-
]
|
398
|
-
)
|
399
|
-
).total_seconds() < RESEND_COOL_DOWN_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS:
|
400
|
-
output_content = get_api_output_in_standard_format(
|
401
|
-
message=messages["GENERIC_400"],
|
402
|
-
log="verification code already exists and was sent within the cooldown period.",
|
403
|
-
)
|
404
|
-
raise HTTPException(
|
405
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
406
|
-
detail=output_content,
|
407
|
-
)
|
408
|
-
|
409
|
-
"""
|
410
|
-
main process
|
411
|
-
"""
|
412
|
-
verification_code = random.randint(
|
413
|
-
10 ** (NUMBER_OF_DIGITS_IN_EMAIL_VERIFICATION_CODE - 1),
|
414
|
-
10**NUMBER_OF_DIGITS_IN_EMAIL_VERIFICATION_CODE - 1,
|
415
|
-
)
|
416
|
-
# hash the verification code
|
417
|
-
hashed_verification_code = bcrypt.hashpw(
|
418
|
-
str(verification_code).encode("utf-8"), bcrypt.gensalt()
|
419
|
-
).decode("utf-8")
|
420
|
-
expires_at = datetime.now(timezone.utc) + timedelta(
|
421
|
-
seconds=EXPIRY_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS
|
422
|
-
)
|
423
|
-
# add verification code to UserVerification code table
|
424
|
-
global_object_square_database_helper.insert_rows_v0(
|
425
|
-
database_name=global_string_database_name,
|
426
|
-
schema_name=global_string_schema_name,
|
427
|
-
table_name=UserVerificationCode.__tablename__,
|
428
|
-
data=[
|
429
|
-
{
|
430
|
-
UserVerificationCode.user_id.name: user_id,
|
431
|
-
UserVerificationCode.user_verification_code_type.name: VerificationCodeTypeEnum.EMAIL_VERIFICATION.value,
|
432
|
-
UserVerificationCode.user_verification_code_hash.name: hashed_verification_code,
|
433
|
-
UserVerificationCode.user_verification_code_expires_at.name: expires_at.strftime(
|
434
|
-
"%Y-%m-%d %H:%M:%S.%f+00"
|
435
|
-
),
|
436
|
-
}
|
437
|
-
],
|
438
|
-
)
|
439
|
-
# send verification email
|
440
|
-
if (
|
441
|
-
user_profile_data[UserProfile.user_profile_first_name.name]
|
442
|
-
and user_profile_data[UserProfile.user_profile_last_name.name]
|
443
|
-
):
|
444
|
-
user_to_name = f"{user_profile_data[UserProfile.user_profile_first_name.name]} {user_profile_data[UserProfile.user_profile_last_name.name]}"
|
445
|
-
elif user_profile_data[UserProfile.user_profile_first_name.name]:
|
446
|
-
user_to_name = user_profile_data[UserProfile.user_profile_first_name.name]
|
447
|
-
elif user_profile_data[UserProfile.user_profile_last_name.name]:
|
448
|
-
user_to_name = user_profile_data[UserProfile.user_profile_last_name.name]
|
449
|
-
else:
|
450
|
-
user_to_name = ""
|
451
|
-
|
452
|
-
mailgun_response = send_email_using_mailgun(
|
453
|
-
from_email="auth@thepmsquare.com",
|
454
|
-
from_name="square_authentication",
|
455
|
-
to_email=user_profile_data[UserProfile.user_profile_email.name],
|
456
|
-
to_name=user_to_name,
|
457
|
-
subject="Email Verification",
|
458
|
-
body=f"Your verification code is {verification_code}. It will expire in {EXPIRY_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS/60} minutes.",
|
459
|
-
api_key=MAIL_GUN_API_KEY,
|
460
|
-
domain_name="thepmsquare.com",
|
461
|
-
)
|
462
|
-
# add log for email sending
|
463
|
-
global_object_square_database_helper.insert_rows_v0(
|
464
|
-
database_name=global_string_database_name,
|
465
|
-
schema_name=email_schema_name,
|
466
|
-
table_name=EmailLog.__tablename__,
|
467
|
-
data=[
|
468
|
-
{
|
469
|
-
EmailLog.user_id.name: user_id,
|
470
|
-
EmailLog.recipient_email.name: user_profile_data[
|
471
|
-
UserProfile.user_profile_email.name
|
472
|
-
],
|
473
|
-
EmailLog.email_type.name: EmailTypeEnum.VERIFY_EMAIL.value,
|
474
|
-
EmailLog.status.name: EmailStatusEnum.SENT.value,
|
475
|
-
EmailLog.third_party_message_id.name: mailgun_response.get("id"),
|
476
|
-
}
|
477
|
-
],
|
478
|
-
)
|
479
|
-
"""
|
480
|
-
return value
|
481
|
-
"""
|
482
|
-
cooldown_reset_at = datetime.now(timezone.utc) + timedelta(
|
483
|
-
seconds=RESEND_COOL_DOWN_TIME_FOR_EMAIL_VERIFICATION_CODE_IN_SECONDS
|
484
|
-
)
|
485
|
-
output_content = get_api_output_in_standard_format(
|
486
|
-
data={
|
487
|
-
"expires_at": expires_at.isoformat(),
|
488
|
-
"cooldown_reset_at": cooldown_reset_at.isoformat(),
|
489
|
-
},
|
490
|
-
message=messages["GENERIC_ACTION_SUCCESSFUL"],
|
491
|
-
)
|
492
|
-
return JSONResponse(
|
493
|
-
status_code=status.HTTP_200_OK,
|
494
|
-
content=output_content,
|
495
|
-
)
|
496
|
-
except HTTPException as http_exception:
|
497
|
-
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
498
|
-
return JSONResponse(
|
499
|
-
status_code=http_exception.status_code, content=http_exception.detail
|
88
|
+
return util_send_verification_email_v0(
|
89
|
+
access_token=access_token,
|
500
90
|
)
|
91
|
+
except HTTPException as he:
|
92
|
+
global_object_square_logger.logger.error(he, exc_info=True)
|
93
|
+
return JSONResponse(status_code=he.status_code, content=he.detail)
|
501
94
|
except Exception as e:
|
502
|
-
"""
|
503
|
-
rollback logic
|
504
|
-
"""
|
505
95
|
global_object_square_logger.logger.error(e, exc_info=True)
|
506
96
|
output_content = get_api_output_in_standard_format(
|
507
|
-
message=messages["GENERIC_500"],
|
508
|
-
log=str(e),
|
97
|
+
message=messages["GENERIC_500"], log=str(e)
|
509
98
|
)
|
510
99
|
return JSONResponse(
|
511
100
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
@@ -520,147 +109,17 @@ async def validate_email_verification_code_v0(
|
|
520
109
|
):
|
521
110
|
verification_code = body.verification_code
|
522
111
|
try:
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
# validate access token
|
527
|
-
try:
|
528
|
-
local_dict_access_token_payload = get_jwt_payload(
|
529
|
-
access_token, config_str_secret_key_for_access_token
|
530
|
-
)
|
531
|
-
except Exception as error:
|
532
|
-
output_content = get_api_output_in_standard_format(
|
533
|
-
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
534
|
-
)
|
535
|
-
raise HTTPException(
|
536
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
537
|
-
detail=output_content,
|
538
|
-
)
|
539
|
-
user_id = local_dict_access_token_payload["user_id"]
|
540
|
-
|
541
|
-
# validate if user has email in profile
|
542
|
-
user_profile_response = global_object_square_database_helper.get_rows_v0(
|
543
|
-
database_name=global_string_database_name,
|
544
|
-
schema_name=global_string_schema_name,
|
545
|
-
table_name=UserProfile.__tablename__,
|
546
|
-
filters=FiltersV0(
|
547
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
548
|
-
),
|
549
|
-
apply_filters=True,
|
550
|
-
)
|
551
|
-
user_profile_data = user_profile_response["data"]["main"][0]
|
552
|
-
if not user_profile_data.get(UserProfile.user_profile_email.name):
|
553
|
-
output_content = get_api_output_in_standard_format(
|
554
|
-
message=messages["GENERIC_MISSING_REQUIRED_FIELD"],
|
555
|
-
log="email is required to send verification email.",
|
556
|
-
)
|
557
|
-
raise HTTPException(
|
558
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
559
|
-
detail=output_content,
|
560
|
-
)
|
561
|
-
# check if email is already verified
|
562
|
-
if user_profile_data.get(UserProfile.user_profile_email_verified.name):
|
563
|
-
output_content = get_api_output_in_standard_format(
|
564
|
-
message=messages["EMAIL_ALREADY_VERIFIED"]
|
565
|
-
)
|
566
|
-
return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
|
567
|
-
# check for verification code in UserVerificationCode table
|
568
|
-
verification_code_response = global_object_square_database_helper.get_rows_v0(
|
569
|
-
database_name=global_string_database_name,
|
570
|
-
schema_name=global_string_schema_name,
|
571
|
-
table_name=UserVerificationCode.__tablename__,
|
572
|
-
filters=FiltersV0(
|
573
|
-
root={
|
574
|
-
UserVerificationCode.user_id.name: FilterConditionsV0(eq=user_id),
|
575
|
-
UserVerificationCode.user_verification_code_type.name: FilterConditionsV0(
|
576
|
-
eq=VerificationCodeTypeEnum.EMAIL_VERIFICATION.value
|
577
|
-
),
|
578
|
-
UserVerificationCode.user_verification_code_used_at.name: FilterConditionsV0(
|
579
|
-
is_null=True
|
580
|
-
),
|
581
|
-
UserVerificationCode.user_verification_code_expires_at.name: FilterConditionsV0(
|
582
|
-
gte=datetime.now(timezone.utc).strftime(
|
583
|
-
"%Y-%m-%d %H:%M:%S.%f+00"
|
584
|
-
)
|
585
|
-
),
|
586
|
-
}
|
587
|
-
),
|
588
|
-
order_by=[
|
589
|
-
"-" + UserVerificationCode.user_verification_code_created_at.name
|
590
|
-
],
|
591
|
-
limit=1,
|
592
|
-
apply_filters=True,
|
593
|
-
)
|
594
|
-
if len(verification_code_response["data"]["main"]) != 1:
|
595
|
-
output_content = get_api_output_in_standard_format(
|
596
|
-
message=messages["INCORRECT_VERIFICATION_CODE"]
|
597
|
-
)
|
598
|
-
raise HTTPException(
|
599
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
600
|
-
detail=output_content,
|
601
|
-
)
|
602
|
-
"""
|
603
|
-
main process
|
604
|
-
"""
|
605
|
-
|
606
|
-
# check if the latest verification code matches the provided code
|
607
|
-
latest_verification_code_data = verification_code_response["data"]["main"][0]
|
608
|
-
latest_verification_code_hash = latest_verification_code_data[
|
609
|
-
UserVerificationCode.user_verification_code_hash.name
|
610
|
-
]
|
611
|
-
if not bcrypt.checkpw(
|
612
|
-
str(verification_code).encode("utf-8"),
|
613
|
-
latest_verification_code_hash.encode("utf-8"),
|
614
|
-
):
|
615
|
-
output_content = get_api_output_in_standard_format(
|
616
|
-
message=messages["INCORRECT_VERIFICATION_CODE"]
|
617
|
-
)
|
618
|
-
raise HTTPException(
|
619
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
620
|
-
detail=output_content,
|
621
|
-
)
|
622
|
-
# update user profile to mark email as verified
|
623
|
-
email_verified_time = datetime.now(timezone.utc)
|
624
|
-
global_object_square_database_helper.edit_rows_v0(
|
625
|
-
database_name=global_string_database_name,
|
626
|
-
schema_name=global_string_schema_name,
|
627
|
-
table_name=UserProfile.__tablename__,
|
628
|
-
filters=FiltersV0(
|
629
|
-
root={UserProfile.user_id.name: FilterConditionsV0(eq=user_id)}
|
630
|
-
),
|
631
|
-
data={
|
632
|
-
UserProfile.user_profile_email_verified.name: email_verified_time.strftime(
|
633
|
-
"%Y-%m-%d %H:%M:%S.%f+00"
|
634
|
-
),
|
635
|
-
},
|
636
|
-
apply_filters=True,
|
637
|
-
)
|
638
|
-
"""
|
639
|
-
return value
|
640
|
-
"""
|
641
|
-
output_content = get_api_output_in_standard_format(
|
642
|
-
data={
|
643
|
-
UserProfile.user_profile_email_verified.name: email_verified_time.isoformat(),
|
644
|
-
},
|
645
|
-
message=messages["GENERIC_ACTION_SUCCESSFUL"],
|
646
|
-
)
|
647
|
-
return JSONResponse(
|
648
|
-
status_code=status.HTTP_200_OK,
|
649
|
-
content=output_content,
|
650
|
-
)
|
651
|
-
except HTTPException as http_exception:
|
652
|
-
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
653
|
-
return JSONResponse(
|
654
|
-
status_code=http_exception.status_code, content=http_exception.detail
|
112
|
+
return util_validate_email_verification_code_v0(
|
113
|
+
access_token=access_token,
|
114
|
+
verification_code=verification_code,
|
655
115
|
)
|
116
|
+
except HTTPException as he:
|
117
|
+
global_object_square_logger.logger.error(he, exc_info=True)
|
118
|
+
return JSONResponse(status_code=he.status_code, content=he.detail)
|
656
119
|
except Exception as e:
|
657
|
-
"""
|
658
|
-
rollback logic
|
659
|
-
"""
|
660
120
|
global_object_square_logger.logger.error(e, exc_info=True)
|
661
121
|
output_content = get_api_output_in_standard_format(
|
662
|
-
message=messages["GENERIC_500"],
|
663
|
-
log=str(e),
|
122
|
+
message=messages["GENERIC_500"], log=str(e)
|
664
123
|
)
|
665
124
|
return JSONResponse(
|
666
125
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|