square-authentication 2.0.0__py3-none-any.whl → 3.0.0__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.
@@ -8,9 +8,10 @@ messages = {
8
8
  "USERNAME_ALREADY_EXISTS": "the username you entered is already taken. please choose a different one.",
9
9
  "INCORRECT_ACCESS_TOKEN": "the access token provided is invalid or expired.",
10
10
  "INCORRECT_REFRESH_TOKEN": "the refresh token provided is invalid or expired.",
11
- "GENERIC_READ_SUCCESSFUL": "data retrieved successfully.",
12
11
  "GENERIC_CREATION_SUCCESSFUL": "records created successfully.",
12
+ "GENERIC_READ_SUCCESSFUL": "data retrieved successfully.",
13
13
  "GENERIC_UPDATE_SUCCESSFUL": "your information has been updated successfully.",
14
+ "GENERIC_DELETE_SUCCESSFUL": "your records have been deleted successfully.",
14
15
  "GENERIC_400": "the request is invalid or cannot be processed.",
15
16
  "GENERIC_500": "an internal server error occurred. please try again later.",
16
17
  }
File without changes
@@ -0,0 +1,24 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class RegisterUsernameV0(BaseModel):
7
+ username: str
8
+ password: str
9
+ app_id: Optional[int] = None
10
+
11
+
12
+ class LoginUsernameV0(BaseModel):
13
+ username: str
14
+ password: str
15
+ app_id: int
16
+
17
+
18
+ class DeleteUserV0(BaseModel):
19
+ password: str
20
+
21
+
22
+ class UpdatePasswordV0(BaseModel):
23
+ old_password: str
24
+ new_password: str
@@ -1,6 +1,5 @@
1
1
  from datetime import datetime, timedelta, timezone
2
2
  from typing import Annotated, List
3
- from uuid import UUID
4
3
 
5
4
  import bcrypt
6
5
  import jwt
@@ -36,6 +35,12 @@ from square_authentication.configuration import (
36
35
  config_str_square_database_protocol,
37
36
  )
38
37
  from square_authentication.messages import messages
38
+ from square_authentication.pydantic_models.core import (
39
+ RegisterUsernameV0,
40
+ LoginUsernameV0,
41
+ DeleteUserV0,
42
+ UpdatePasswordV0,
43
+ )
39
44
  from square_authentication.utils.token import get_jwt_payload
40
45
 
41
46
  router = APIRouter(
@@ -51,8 +56,17 @@ global_object_square_database_helper = SquareDatabaseHelper(
51
56
 
52
57
  @router.post("/register_username/v0")
53
58
  @global_object_square_logger.async_auto_logger
54
- async def register_username_v0(username: str, password: str):
59
+ async def register_username_v0(
60
+ body: RegisterUsernameV0,
61
+ ):
62
+ username = body.username
63
+ password = body.password
64
+ app_id = body.app_id
65
+
55
66
  local_str_user_id = None
67
+ local_str_access_token = None
68
+ local_str_refresh_token = None
69
+
56
70
  username = username.lower()
57
71
  try:
58
72
  """
@@ -117,13 +131,77 @@ async def register_username_v0(username: str, password: str):
117
131
  schema_name=global_string_schema_name,
118
132
  table_name=UserCredential.__tablename__,
119
133
  )
134
+ if app_id is not None:
135
+ # assign app to user
136
+ global_object_square_database_helper.insert_rows_v0(
137
+ database_name=global_string_database_name,
138
+ schema_name=global_string_schema_name,
139
+ table_name=UserApp.__tablename__,
140
+ data=[
141
+ {
142
+ UserApp.user_id.name: local_str_user_id,
143
+ UserApp.app_id.name: app_id,
144
+ }
145
+ ],
146
+ )
147
+
148
+ # return new access token and refresh token
149
+ # create access token
150
+ local_dict_access_token_payload = {
151
+ "app_id": app_id,
152
+ "user_id": local_str_user_id,
153
+ "exp": datetime.now(timezone.utc)
154
+ + timedelta(minutes=config_int_access_token_valid_minutes),
155
+ }
156
+ local_str_access_token = jwt.encode(
157
+ local_dict_access_token_payload,
158
+ config_str_secret_key_for_access_token,
159
+ )
160
+
161
+ # create refresh token
162
+ local_object_refresh_token_expiry_time = datetime.now(
163
+ timezone.utc
164
+ ) + timedelta(minutes=config_int_refresh_token_valid_minutes)
120
165
 
166
+ local_dict_refresh_token_payload = {
167
+ "app_id": app_id,
168
+ "user_id": local_str_user_id,
169
+ "exp": local_object_refresh_token_expiry_time,
170
+ }
171
+ local_str_refresh_token = jwt.encode(
172
+ local_dict_refresh_token_payload,
173
+ config_str_secret_key_for_refresh_token,
174
+ )
175
+ # entry in user session table
176
+ global_object_square_database_helper.insert_rows_v0(
177
+ data=[
178
+ {
179
+ UserSession.user_id.name: local_str_user_id,
180
+ UserSession.app_id.name: app_id,
181
+ UserSession.user_session_refresh_token.name: local_str_refresh_token,
182
+ UserSession.user_session_expiry_time.name: local_object_refresh_token_expiry_time.strftime(
183
+ "%Y-%m-%d %H:%M:%S.%f+00"
184
+ ),
185
+ }
186
+ ],
187
+ database_name=global_string_database_name,
188
+ schema_name=global_string_schema_name,
189
+ table_name=UserSession.__tablename__,
190
+ )
121
191
  """
122
192
  return value
123
193
  """
124
194
  output_content = get_api_output_in_standard_format(
125
195
  message=messages["REGISTRATION_SUCCESSFUL"],
126
- data={"main": {"user_id": local_str_user_id, "username": username}},
196
+ data={
197
+ "main": {
198
+ "user_id": local_str_user_id,
199
+ "username": username,
200
+ "app_id": app_id,
201
+ "access_token": local_str_access_token,
202
+ "refresh_token": local_str_refresh_token,
203
+ },
204
+ },
127
205
  )
128
206
  return JSONResponse(
129
207
  status_code=status.HTTP_201_CREATED,
@@ -156,32 +234,29 @@ async def register_username_v0(username: str, password: str):
156
234
  )
157
235
 
158
236
 
159
- @router.get("/get_user_app_ids/v0")
237
+ @router.get("/get_user_details/v0")
160
238
  @global_object_square_logger.async_auto_logger
161
- async def get_user_app_ids_v0(user_id: UUID):
239
+ async def get_user_details_v0(
240
+ access_token: Annotated[str, Header()],
241
+ ):
162
242
  try:
163
- local_string_user_id = str(user_id)
164
243
  """
165
244
  validation
166
245
  """
167
-
168
- local_list_response_user = global_object_square_database_helper.get_rows_v0(
169
- database_name=global_string_database_name,
170
- schema_name=global_string_schema_name,
171
- table_name=User.__tablename__,
172
- filters=FiltersV0(
173
- {User.user_id.name: FilterConditionsV0(eq=local_string_user_id)}
174
- ),
175
- )["data"]["main"]
176
- if len(local_list_response_user) != 1:
246
+ # validate access token
247
+ try:
248
+ local_dict_access_token_payload = get_jwt_payload(
249
+ access_token, config_str_secret_key_for_access_token
250
+ )
251
+ except Exception as error:
177
252
  output_content = get_api_output_in_standard_format(
178
- message=messages["INCORRECT_USER_ID"],
179
- log=f"invalid user_id: {local_string_user_id}",
253
+ message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
180
254
  )
181
- raise HTTPException(
182
- status_code=status.HTTP_404_NOT_FOUND,
183
- detail=output_content,
255
+ return JSONResponse(
256
+ status_code=status.HTTP_400_BAD_REQUEST,
257
+ content=output_content,
184
258
  )
259
+ user_id = local_dict_access_token_payload["user_id"]
185
260
  """
186
261
  main process
187
262
  """
@@ -189,18 +264,59 @@ async def get_user_app_ids_v0(user_id: UUID):
189
264
  database_name=global_string_database_name,
190
265
  schema_name=global_string_schema_name,
191
266
  table_name=UserApp.__tablename__,
192
- filters=FiltersV0(
193
- {UserApp.user_id.name: FilterConditionsV0(eq=local_string_user_id)}
194
- ),
267
+ filters=FiltersV0({UserApp.user_id.name: FilterConditionsV0(eq=user_id)}),
195
268
  )["data"]["main"]
269
+ local_list_response_user_credentials = (
270
+ global_object_square_database_helper.get_rows_v0(
271
+ database_name=global_string_database_name,
272
+ schema_name=global_string_schema_name,
273
+ table_name=UserCredential.__tablename__,
274
+ filters=FiltersV0(
275
+ {UserCredential.user_id.name: FilterConditionsV0(eq=user_id)}
276
+ ),
277
+ )["data"]["main"]
278
+ )
279
+ # not putting filter for expiry refresh tokens
280
+ local_list_response_user_sessions = (
281
+ global_object_square_database_helper.get_rows_v0(
282
+ database_name=global_string_database_name,
283
+ schema_name=global_string_schema_name,
284
+ table_name=UserSession.__tablename__,
285
+ filters=FiltersV0(
286
+ {
287
+ UserSession.user_id.name: FilterConditionsV0(eq=user_id),
288
+ }
289
+ ),
290
+ )["data"]["main"]
291
+ )
196
292
  """
197
293
  return value
198
294
  """
295
+ return_this = {
296
+ "user_id": user_id,
297
+ "credentials": {
298
+ "username": local_list_response_user_credentials[0][
299
+ UserCredential.user_credential_username.name
300
+ ],
301
+ },
302
+ "apps": [x[UserApp.app_id.name] for x in local_list_response_user_app],
303
+ "sessions": [
304
+ {
305
+ "app_id": x[UserApp.app_id.name],
306
+ "sessions": len(
307
+ [
308
+ y
309
+ for y in local_list_response_user_sessions
310
+ if y[UserSession.app_id.name] == x[UserApp.app_id.name]
311
+ ]
312
+ ),
313
+ }
314
+ for x in local_list_response_user_app
315
+ ],
316
+ }
199
317
  output_content = get_api_output_in_standard_format(
200
318
  message=messages["GENERIC_READ_SUCCESSFUL"],
201
- data={
202
- "main": [x[UserApp.app_id.name] for x in local_list_response_user_app]
203
- },
319
+ data={"main": return_this},
204
320
  )
205
321
  return JSONResponse(
206
322
  status_code=status.HTTP_200_OK,
@@ -228,15 +344,29 @@ async def get_user_app_ids_v0(user_id: UUID):
228
344
  @router.patch("/update_user_app_ids/v0")
229
345
  @global_object_square_logger.async_auto_logger
230
346
  async def update_user_app_ids_v0(
231
- user_id: UUID,
347
+ access_token: Annotated[str, Header()],
232
348
  app_ids_to_add: List[int],
233
349
  app_ids_to_remove: List[int],
234
350
  ):
235
351
  try:
236
- local_string_user_id = str(user_id)
352
+
237
353
  """
238
354
  validation
239
355
  """
356
+ # validate access token
357
+ try:
358
+ local_dict_access_token_payload = get_jwt_payload(
359
+ access_token, config_str_secret_key_for_access_token
360
+ )
361
+ except Exception as error:
362
+ output_content = get_api_output_in_standard_format(
363
+ message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
364
+ )
365
+ return JSONResponse(
366
+ status_code=status.HTTP_400_BAD_REQUEST,
367
+ content=output_content,
368
+ )
369
+ user_id = local_dict_access_token_payload["user_id"]
240
370
 
241
371
  app_ids_to_add = list(set(app_ids_to_add))
242
372
  app_ids_to_remove = list(set(app_ids_to_remove))
@@ -252,27 +382,6 @@ async def update_user_app_ids_v0(
252
382
  status_code=status.HTTP_400_BAD_REQUEST,
253
383
  detail=output_content,
254
384
  )
255
- # validate access token
256
- # TBD
257
-
258
- # check if user id is in user table
259
- local_list_response_user = global_object_square_database_helper.get_rows_v0(
260
- database_name=global_string_database_name,
261
- schema_name=global_string_schema_name,
262
- table_name=User.__tablename__,
263
- filters=FiltersV0(
264
- {User.user_id.name: FilterConditionsV0(eq=local_string_user_id)}
265
- ),
266
- )["data"]["main"]
267
- if len(local_list_response_user) != 1:
268
- output_content = get_api_output_in_standard_format(
269
- message=messages["INCORRECT_USER_ID"],
270
- log=f"invalid user_id: {local_string_user_id}",
271
- )
272
- raise HTTPException(
273
- status_code=status.HTTP_404_NOT_FOUND,
274
- detail=output_content,
275
- )
276
385
 
277
386
  # check if all app_ids are valid
278
387
  local_list_all_app_ids = [*app_ids_to_add, *app_ids_to_remove]
@@ -305,13 +414,11 @@ async def update_user_app_ids_v0(
305
414
  database_name=global_string_database_name,
306
415
  schema_name=global_string_schema_name,
307
416
  table_name=UserApp.__tablename__,
308
- filters=FiltersV0(
309
- {UserApp.user_id.name: FilterConditionsV0(eq=local_string_user_id)}
310
- ),
417
+ filters=FiltersV0({UserApp.user_id.name: FilterConditionsV0(eq=user_id)}),
311
418
  )["data"]["main"]
312
419
  local_list_new_app_ids = [
313
420
  {
314
- UserApp.user_id.name: local_string_user_id,
421
+ UserApp.user_id.name: user_id,
315
422
  UserApp.app_id.name: x,
316
423
  }
317
424
  for x in app_ids_to_add
@@ -333,22 +440,19 @@ async def update_user_app_ids_v0(
333
440
  table_name=UserApp.__tablename__,
334
441
  filters=FiltersV0(
335
442
  {
336
- UserApp.user_id.name: FilterConditionsV0(
337
- eq=local_string_user_id
338
- ),
443
+ UserApp.user_id.name: FilterConditionsV0(eq=user_id),
339
444
  UserApp.app_id.name: FilterConditionsV0(eq=app_id),
340
445
  }
341
446
  ),
342
447
  )
448
+ # logout user from removed apps
343
449
  global_object_square_database_helper.delete_rows_v0(
344
450
  database_name=global_string_database_name,
345
451
  schema_name=global_string_schema_name,
346
452
  table_name=UserSession.__tablename__,
347
453
  filters=FiltersV0(
348
454
  {
349
- UserSession.user_id.name: FilterConditionsV0(
350
- eq=local_string_user_id
351
- ),
455
+ UserSession.user_id.name: FilterConditionsV0(eq=user_id),
352
456
  UserSession.app_id.name: FilterConditionsV0(eq=app_id),
353
457
  }
354
458
  ),
@@ -362,9 +466,7 @@ async def update_user_app_ids_v0(
362
466
  database_name=global_string_database_name,
363
467
  schema_name=global_string_schema_name,
364
468
  table_name=UserApp.__tablename__,
365
- filters=FiltersV0(
366
- {UserApp.user_id.name: FilterConditionsV0(eq=local_string_user_id)}
367
- ),
469
+ filters=FiltersV0({UserApp.user_id.name: FilterConditionsV0(eq=user_id)}),
368
470
  )["data"]["main"]
369
471
  output_content = get_api_output_in_standard_format(
370
472
  message=messages["GENERIC_UPDATE_SUCCESSFUL"],
@@ -397,7 +499,10 @@ async def update_user_app_ids_v0(
397
499
 
398
500
  @router.get("/login_username/v0")
399
501
  @global_object_square_logger.async_auto_logger
400
- async def login_username_v0(username: str, password: str, app_id: int):
502
+ async def login_username_v0(body: LoginUsernameV0):
503
+ username = body.username
504
+ password = body.password
505
+ app_id = body.app_id
401
506
  username = username.lower()
402
507
  try:
403
508
  """
@@ -554,54 +659,14 @@ async def login_username_v0(username: str, password: str, app_id: int):
554
659
  @router.get("/generate_access_token/v0")
555
660
  @global_object_square_logger.async_auto_logger
556
661
  async def generate_access_token_v0(
557
- user_id: str, app_id: int, refresh_token: Annotated[str, Header()]
662
+ refresh_token: Annotated[str, Header()],
558
663
  ):
559
664
  try:
560
665
  """
561
666
  validation
562
667
  """
563
- # validate user_id
564
- local_list_user_response = global_object_square_database_helper.get_rows_v0(
565
- database_name=global_string_database_name,
566
- schema_name=global_string_schema_name,
567
- table_name=User.__tablename__,
568
- filters=FiltersV0({User.user_id.name: FilterConditionsV0(eq=user_id)}),
569
- )["data"]["main"]
570
-
571
- if len(local_list_user_response) != 1:
572
- output_content = get_api_output_in_standard_format(
573
- message=messages["INCORRECT_USER_ID"],
574
- log=f"incorrect user_id: {user_id}.",
575
- )
576
- return JSONResponse(
577
- status_code=status.HTTP_400_BAD_REQUEST,
578
- content=output_content,
579
- )
580
- # validate if app_id is assigned to user
581
- # this will also validate if app_id is valid
582
- local_dict_user = local_list_user_response[0]
583
- local_str_user_id = local_dict_user[User.user_id.name]
584
- local_list_user_app_response = global_object_square_database_helper.get_rows_v0(
585
- database_name=global_string_database_name,
586
- schema_name=global_string_schema_name,
587
- table_name=UserApp.__tablename__,
588
- filters=FiltersV0(
589
- {
590
- UserApp.user_id.name: FilterConditionsV0(eq=local_str_user_id),
591
- UserApp.app_id.name: FilterConditionsV0(eq=app_id),
592
- }
593
- ),
594
- )["data"]["main"]
595
- if len(local_list_user_app_response) != 1:
596
- output_content = get_api_output_in_standard_format(
597
- message=messages["GENERIC_400"],
598
- log=f"user_id {local_str_user_id} not assigned to app {app_id}.",
599
- )
600
- return JSONResponse(
601
- status_code=status.HTTP_400_BAD_REQUEST, content=output_content
602
- )
603
668
  # validate refresh token
604
- # validating if a session refresh token exists in the database for provided app id.
669
+ # validating if a session refresh token exists in the database.
605
670
  local_list_user_session_response = (
606
671
  global_object_square_database_helper.get_rows_v0(
607
672
  database_name=global_string_database_name,
@@ -609,11 +674,9 @@ async def generate_access_token_v0(
609
674
  table_name=UserSession.__tablename__,
610
675
  filters=FiltersV0(
611
676
  {
612
- UserSession.user_id.name: FilterConditionsV0(eq=user_id),
613
677
  UserSession.user_session_refresh_token.name: FilterConditionsV0(
614
678
  eq=refresh_token
615
679
  ),
616
- UserSession.app_id.name: FilterConditionsV0(eq=app_id),
617
680
  }
618
681
  ),
619
682
  )["data"]["main"]
@@ -622,7 +685,7 @@ async def generate_access_token_v0(
622
685
  if len(local_list_user_session_response) != 1:
623
686
  output_content = get_api_output_in_standard_format(
624
687
  message=messages["INCORRECT_REFRESH_TOKEN"],
625
- log=f"incorrect refresh token: {refresh_token} for user_id: {user_id} for app_id: {app_id}.",
688
+ log=f"incorrect refresh token: {refresh_token}.",
626
689
  )
627
690
  return JSONResponse(
628
691
  status_code=status.HTTP_400_BAD_REQUEST,
@@ -641,32 +704,13 @@ async def generate_access_token_v0(
641
704
  status_code=status.HTTP_400_BAD_REQUEST,
642
705
  content=output_content,
643
706
  )
644
-
645
- if local_dict_refresh_token_payload["user_id"] != user_id:
646
- output_content = get_api_output_in_standard_format(
647
- message=messages["INCORRECT_REFRESH_TOKEN"],
648
- log=f"refresh token and user_id mismatch.",
649
- )
650
- return JSONResponse(
651
- status_code=status.HTTP_400_BAD_REQUEST,
652
- content=output_content,
653
- )
654
- if local_dict_refresh_token_payload["app_id"] != app_id:
655
- output_content = get_api_output_in_standard_format(
656
- message=messages["INCORRECT_REFRESH_TOKEN"],
657
- log=f"refresh token and app_id mismatch.",
658
- )
659
- return JSONResponse(
660
- status_code=status.HTTP_400_BAD_REQUEST,
661
- content=output_content,
662
- )
663
707
  """
664
708
  main process
665
709
  """
666
710
  # create and send access token
667
711
  local_dict_access_token_payload = {
668
- "app_id": app_id,
669
- "user_id": user_id,
712
+ "app_id": local_dict_refresh_token_payload["app_id"],
713
+ "user_id": local_dict_refresh_token_payload["user_id"],
670
714
  "exp": datetime.now(timezone.utc)
671
715
  + timedelta(minutes=config_int_access_token_valid_minutes),
672
716
  }
@@ -705,15 +749,121 @@ async def generate_access_token_v0(
705
749
  @router.delete("/logout/v0")
706
750
  @global_object_square_logger.async_auto_logger
707
751
  async def logout_v0(
708
- user_id: str,
709
- app_id: int,
710
- access_token: Annotated[str, Header()],
711
752
  refresh_token: Annotated[str, Header()],
712
753
  ):
713
754
  try:
714
755
  """
715
756
  validation
716
757
  """
758
+ # validate refresh token
759
+ # validating if a session refresh token exists in the database.
760
+ local_list_user_session_response = (
761
+ global_object_square_database_helper.get_rows_v0(
762
+ database_name=global_string_database_name,
763
+ schema_name=global_string_schema_name,
764
+ table_name=UserSession.__tablename__,
765
+ filters=FiltersV0(
766
+ {
767
+ UserSession.user_session_refresh_token.name: FilterConditionsV0(
768
+ eq=refresh_token
769
+ ),
770
+ }
771
+ ),
772
+ )["data"]["main"]
773
+ )
774
+
775
+ if len(local_list_user_session_response) != 1:
776
+ output_content = get_api_output_in_standard_format(
777
+ message=messages["INCORRECT_REFRESH_TOKEN"],
778
+ log=f"incorrect refresh token: {refresh_token}.",
779
+ )
780
+ return JSONResponse(
781
+ status_code=status.HTTP_400_BAD_REQUEST,
782
+ content=output_content,
783
+ )
784
+ # validating if the refresh token is valid, active and of the same user.
785
+ try:
786
+ local_dict_refresh_token_payload = get_jwt_payload(
787
+ refresh_token, config_str_secret_key_for_refresh_token
788
+ )
789
+ except Exception as error:
790
+ output_content = get_api_output_in_standard_format(
791
+ message=messages["INCORRECT_REFRESH_TOKEN"],
792
+ log=str(error),
793
+ )
794
+ return JSONResponse(
795
+ status_code=status.HTTP_400_BAD_REQUEST,
796
+ content=output_content,
797
+ )
798
+ # ======================================================================================
799
+ # NOTE: if refresh token has expired no need to delete it during this call
800
+ # ======================================================================================
801
+ """
802
+ main process
803
+ """
804
+ # delete session for user
805
+ global_object_square_database_helper.delete_rows_v0(
806
+ database_name=global_string_database_name,
807
+ schema_name=global_string_schema_name,
808
+ table_name=UserSession.__tablename__,
809
+ filters=FiltersV0(
810
+ {
811
+ UserSession.user_session_refresh_token.name: FilterConditionsV0(
812
+ eq=refresh_token
813
+ ),
814
+ }
815
+ ),
816
+ )
817
+ """
818
+ return value
819
+ """
820
+ output_content = get_api_output_in_standard_format(
821
+ message=messages["LOGOUT_SUCCESSFUL"],
822
+ )
823
+ return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
824
+ except HTTPException as http_exception:
825
+ return JSONResponse(
826
+ status_code=http_exception.status_code, content=http_exception.detail
827
+ )
828
+ except Exception as e:
829
+ """
830
+ rollback logic
831
+ """
832
+ global_object_square_logger.logger.error(e, exc_info=True)
833
+ output_content = get_api_output_in_standard_format(
834
+ message=messages["GENERIC_500"],
835
+ log=str(e),
836
+ )
837
+ return JSONResponse(
838
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
839
+ )
840
+
841
+
842
+ @router.patch("/update_username/v0")
843
+ @global_object_square_logger.async_auto_logger
844
+ async def update_username_v0(
845
+ new_username: str,
846
+ access_token: Annotated[str, Header()],
847
+ ):
848
+ try:
849
+ """
850
+ validation
851
+ """
852
+ # validate access token
853
+ try:
854
+ local_dict_access_token_payload = get_jwt_payload(
855
+ access_token, config_str_secret_key_for_access_token
856
+ )
857
+ except Exception as error:
858
+ output_content = get_api_output_in_standard_format(
859
+ message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
860
+ )
861
+ return JSONResponse(
862
+ status_code=status.HTTP_400_BAD_REQUEST,
863
+ content=output_content,
864
+ )
865
+ user_id = local_dict_access_token_payload["user_id"]
866
+
717
867
  # validate user_id
718
868
  local_list_user_response = global_object_square_database_helper.get_rows_v0(
719
869
  database_name=global_string_database_name,
@@ -736,62 +886,192 @@ async def logout_v0(
736
886
  content=output_content,
737
887
  )
738
888
 
739
- # validate if app_id is assigned to user
740
- # this will also validate if app_id is valid
741
- local_dict_user = local_list_user_response[0]
742
- local_str_user_id = local_dict_user[User.user_id.name]
743
- local_list_user_app_response = global_object_square_database_helper.get_rows_v0(
889
+ # validate new username
890
+ local_list_user_credentials_response = global_object_square_database_helper.get_rows_v0(
744
891
  database_name=global_string_database_name,
745
892
  schema_name=global_string_schema_name,
746
- table_name=UserApp.__tablename__,
893
+ table_name=UserCredential.__tablename__,
747
894
  filters=FiltersV0(
748
895
  {
749
- UserApp.user_id.name: FilterConditionsV0(eq=local_str_user_id),
750
- UserApp.app_id.name: FilterConditionsV0(eq=app_id),
896
+ UserCredential.user_credential_username.name: FilterConditionsV0(
897
+ eq=new_username
898
+ ),
751
899
  }
752
900
  ),
753
- )["data"]["main"]
754
- if len(local_list_user_app_response) != 1:
901
+ )[
902
+ "data"
903
+ ][
904
+ "main"
905
+ ]
906
+ if len(local_list_user_credentials_response) != 0:
755
907
  output_content = get_api_output_in_standard_format(
756
- message=messages["GENERIC_400"],
757
- log=f"user_id {local_str_user_id} not assigned to app {app_id}.",
908
+ message=messages["USERNAME_ALREADY_EXISTS"],
909
+ log=f"{new_username} is taken.",
758
910
  )
759
911
  return JSONResponse(
760
- status_code=status.HTTP_400_BAD_REQUEST, content=output_content
912
+ status_code=status.HTTP_409_CONFLICT,
913
+ content=output_content,
761
914
  )
915
+ """
916
+ main process
917
+ """
918
+ # edit the username
919
+ global_object_square_database_helper.edit_rows_v0(
920
+ database_name=global_string_database_name,
921
+ schema_name=global_string_schema_name,
922
+ table_name=UserCredential.__tablename__,
923
+ filters=FiltersV0(
924
+ {
925
+ UserCredential.user_id.name: FilterConditionsV0(eq=user_id),
926
+ }
927
+ ),
928
+ data={
929
+ UserCredential.user_credential_username.name: new_username,
930
+ },
931
+ )
932
+ """
933
+ return value
934
+ """
935
+ output_content = get_api_output_in_standard_format(
936
+ data={"main": {"user_id": user_id, "username": new_username}},
937
+ message=messages["GENERIC_UPDATE_SUCCESSFUL"],
938
+ )
939
+ return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
940
+ except HTTPException as http_exception:
941
+ return JSONResponse(
942
+ status_code=http_exception.status_code, content=http_exception.detail
943
+ )
944
+ except Exception as e:
945
+ """
946
+ rollback logic
947
+ """
948
+ global_object_square_logger.logger.error(e, exc_info=True)
949
+ output_content = get_api_output_in_standard_format(
950
+ message=messages["GENERIC_500"],
951
+ log=str(e),
952
+ )
953
+ return JSONResponse(
954
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
955
+ )
762
956
 
763
- # validate refresh token
764
- # validating if a session refresh token exists in the database.
765
- local_list_user_session_response = (
957
+
958
+ @router.delete("/delete_user/v0")
959
+ @global_object_square_logger.async_auto_logger
960
+ async def delete_user_v0(
961
+ body: DeleteUserV0,
962
+ access_token: Annotated[str, Header()],
963
+ ):
964
+ password = body.password
965
+ try:
966
+ """
967
+ validation
968
+ """
969
+ # validate access token
970
+ try:
971
+ local_dict_access_token_payload = get_jwt_payload(
972
+ access_token, config_str_secret_key_for_access_token
973
+ )
974
+ except Exception as error:
975
+ output_content = get_api_output_in_standard_format(
976
+ message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
977
+ )
978
+ return JSONResponse(
979
+ status_code=status.HTTP_400_BAD_REQUEST,
980
+ content=output_content,
981
+ )
982
+ user_id = local_dict_access_token_payload["user_id"]
983
+
984
+ # validate user_id
985
+ local_list_authentication_user_response = (
766
986
  global_object_square_database_helper.get_rows_v0(
767
987
  database_name=global_string_database_name,
768
988
  schema_name=global_string_schema_name,
769
- table_name=UserSession.__tablename__,
989
+ table_name=UserCredential.__tablename__,
770
990
  filters=FiltersV0(
771
- {
772
- UserSession.user_id.name: FilterConditionsV0(eq=user_id),
773
- UserSession.user_session_refresh_token.name: FilterConditionsV0(
774
- eq=refresh_token
775
- ),
776
- UserSession.app_id.name: FilterConditionsV0(eq=app_id),
777
- }
991
+ {UserCredential.user_id.name: FilterConditionsV0(eq=user_id)}
778
992
  ),
779
993
  )["data"]["main"]
780
994
  )
995
+ if len(local_list_authentication_user_response) != 1:
996
+ output_content = get_api_output_in_standard_format(
997
+ message=messages["INCORRECT_USER_ID"],
998
+ log=f"incorrect user_id: {user_id}.",
999
+ )
1000
+ return JSONResponse(
1001
+ status_code=status.HTTP_400_BAD_REQUEST, content=output_content
1002
+ )
781
1003
 
782
- if len(local_list_user_session_response) != 1:
1004
+ # validate password
1005
+ local_dict_user = local_list_authentication_user_response[0]
1006
+ if not (
1007
+ bcrypt.checkpw(
1008
+ password.encode("utf-8"),
1009
+ local_dict_user[
1010
+ UserCredential.user_credential_hashed_password.name
1011
+ ].encode("utf-8"),
1012
+ )
1013
+ ):
783
1014
  output_content = get_api_output_in_standard_format(
784
- message=messages["INCORRECT_REFRESH_TOKEN"],
785
- log=f"incorrect refresh token: {refresh_token} for user_id: {user_id} for app_id: {app_id}.",
1015
+ message=messages["INCORRECT_PASSWORD"],
1016
+ log=f"incorrect password for user_id {user_id}.",
786
1017
  )
787
1018
  return JSONResponse(
788
1019
  status_code=status.HTTP_400_BAD_REQUEST,
789
1020
  content=output_content,
790
1021
  )
791
- # **not validating if the refresh token is valid, active, of the same user and of the provided app id.**
1022
+ """
1023
+ main process
1024
+ """
1025
+ # delete the user.
1026
+ global_object_square_database_helper.delete_rows_v0(
1027
+ database_name=global_string_database_name,
1028
+ schema_name=global_string_schema_name,
1029
+ table_name=User.__tablename__,
1030
+ filters=FiltersV0(
1031
+ {
1032
+ User.user_id.name: FilterConditionsV0(eq=user_id),
1033
+ }
1034
+ ),
1035
+ )
1036
+ """
1037
+ return value
1038
+ """
1039
+ output_content = get_api_output_in_standard_format(
1040
+ message=messages["GENERIC_DELETE_SUCCESSFUL"],
1041
+ log=f"user_id: {user_id} deleted successfully.",
1042
+ )
1043
+ return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
1044
+ except HTTPException as http_exception:
1045
+ return JSONResponse(
1046
+ status_code=http_exception.status_code, content=http_exception.detail
1047
+ )
1048
+ except Exception as e:
1049
+ """
1050
+ rollback logic
1051
+ """
1052
+ global_object_square_logger.logger.error(e, exc_info=True)
1053
+ output_content = get_api_output_in_standard_format(
1054
+ message=messages["GENERIC_500"],
1055
+ log=str(e),
1056
+ )
1057
+ return JSONResponse(
1058
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
1059
+ )
792
1060
 
1061
+
1062
+ @router.patch("/update_password/v0")
1063
+ @global_object_square_logger.async_auto_logger
1064
+ async def update_password_v0(
1065
+ body: UpdatePasswordV0,
1066
+ access_token: Annotated[str, Header()],
1067
+ ):
1068
+ old_password = body.old_password
1069
+ new_password = body.new_password
1070
+ try:
1071
+ """
1072
+ validation
1073
+ """
793
1074
  # validate access token
794
- # validating if the access token is valid, active, of the same user and of the provided app.
795
1075
  try:
796
1076
  local_dict_access_token_payload = get_jwt_payload(
797
1077
  access_token, config_str_secret_key_for_access_token
@@ -804,53 +1084,72 @@ async def logout_v0(
804
1084
  status_code=status.HTTP_400_BAD_REQUEST,
805
1085
  content=output_content,
806
1086
  )
807
- if local_dict_access_token_payload["user_id"] != user_id:
1087
+ user_id = local_dict_access_token_payload["user_id"]
1088
+
1089
+ # validate user_id
1090
+ local_list_authentication_user_response = (
1091
+ global_object_square_database_helper.get_rows_v0(
1092
+ database_name=global_string_database_name,
1093
+ schema_name=global_string_schema_name,
1094
+ table_name=UserCredential.__tablename__,
1095
+ filters=FiltersV0(
1096
+ {UserCredential.user_id.name: FilterConditionsV0(eq=user_id)}
1097
+ ),
1098
+ )["data"]["main"]
1099
+ )
1100
+ if len(local_list_authentication_user_response) != 1:
808
1101
  output_content = get_api_output_in_standard_format(
809
- message=messages["INCORRECT_ACCESS_TOKEN"],
810
- log=f"access token and user_id mismatch.",
1102
+ message=messages["INCORRECT_USER_ID"],
1103
+ log=f"incorrect user_id: {user_id}.",
811
1104
  )
812
1105
  return JSONResponse(
813
- status_code=status.HTTP_400_BAD_REQUEST,
814
- content=output_content,
1106
+ status_code=status.HTTP_400_BAD_REQUEST, content=output_content
815
1107
  )
816
- if local_dict_access_token_payload["app_id"] != app_id:
1108
+
1109
+ # validate password
1110
+ local_dict_user = local_list_authentication_user_response[0]
1111
+ if not (
1112
+ bcrypt.checkpw(
1113
+ old_password.encode("utf-8"),
1114
+ local_dict_user[
1115
+ UserCredential.user_credential_hashed_password.name
1116
+ ].encode("utf-8"),
1117
+ )
1118
+ ):
817
1119
  output_content = get_api_output_in_standard_format(
818
- message=messages["INCORRECT_ACCESS_TOKEN"],
819
- log=f"access token and app_id mismatch.",
1120
+ message=messages["INCORRECT_PASSWORD"],
1121
+ log=f"incorrect password for user_id {user_id}.",
820
1122
  )
821
1123
  return JSONResponse(
822
1124
  status_code=status.HTTP_400_BAD_REQUEST,
823
1125
  content=output_content,
824
1126
  )
825
- # ======================================================================================
826
-
827
- # NOTE: if both access token and refresh token have expired for a user,
828
- # it can be assumed that user session only needs to be removed from the front end.
829
-
830
- # ======================================================================================
831
1127
  """
832
1128
  main process
833
1129
  """
834
- # delete session for user
835
- global_object_square_database_helper.delete_rows_v0(
1130
+ # delete the user.
1131
+ local_str_hashed_password = bcrypt.hashpw(
1132
+ new_password.encode("utf-8"), bcrypt.gensalt()
1133
+ ).decode("utf-8")
1134
+ global_object_square_database_helper.edit_rows_v0(
836
1135
  database_name=global_string_database_name,
837
1136
  schema_name=global_string_schema_name,
838
- table_name=UserSession.__tablename__,
1137
+ table_name=UserCredential.__tablename__,
839
1138
  filters=FiltersV0(
840
1139
  {
841
- UserSession.user_id.name: FilterConditionsV0(eq=user_id),
842
- UserSession.user_session_refresh_token.name: FilterConditionsV0(
843
- eq=refresh_token
844
- ),
845
- UserSession.app_id.name: FilterConditionsV0(eq=app_id),
1140
+ UserCredential.user_id.name: FilterConditionsV0(eq=user_id),
846
1141
  }
847
1142
  ),
1143
+ data={
1144
+ UserCredential.user_credential_hashed_password.name: local_str_hashed_password,
1145
+ },
848
1146
  )
849
1147
  """
850
1148
  return value
851
1149
  """
852
1150
  output_content = get_api_output_in_standard_format(
853
- message=messages["LOGOUT_SUCCESSFUL"],
1151
+ message=messages["GENERIC_UPDATE_SUCCESSFUL"],
1152
+ log=f"password for user_id: {user_id} updated successfully.",
854
1153
  )
855
1154
  return JSONResponse(status_code=status.HTTP_200_OK, content=output_content)
856
1155
  except HTTPException as http_exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: square-authentication
3
- Version: 2.0.0
3
+ Version: 3.0.0
4
4
  Summary: authentication layer for my personal server.
5
5
  Home-page: https://github.com/thepmsquare/square_authentication
6
6
  Author: thePmSquare
@@ -43,6 +43,19 @@ pip install square_authentication
43
43
 
44
44
  ## changelog
45
45
 
46
+ ### v3.0.0
47
+
48
+ - added new endpoints
49
+ - /update_username/v0
50
+ - /delete_user/v0
51
+ - /update_password/v0
52
+ - move data in password related endpoints to request body from params.
53
+ - /register_username/v0 now takes in app_id as optional parameter to assign user to that app and create session for it.
54
+ - /generate_access_token/v0 now only needs refresh token (removed validation).
55
+ - /logout/v0 now only needs refresh token (removed validation).
56
+ - /update_user_app_ids/v0 now only updates ids for self (user). added access token as input param and removed user_id.
57
+ - /get_user_app_ids/v0 is now /get_user_details/v0 with access token as the only input param.
58
+
46
59
  ### v2.0.0
47
60
 
48
61
  - authentication module needs to be used across applications so
@@ -1,15 +1,17 @@
1
1
  square_authentication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  square_authentication/configuration.py,sha256=i0uNtNSQd-n1rBxFM6jsIz1Wy3d090RcdTViSqHKc7Y,2973
3
3
  square_authentication/main.py,sha256=JK9KBmN73KL8EpKrXrjrwwf37bmC4AXrFHtfl2roYwQ,1636
4
- square_authentication/messages.py,sha256=NHJLnaB-qj69VJrMkmbMXDHJUnakQLht14ZBrw4dGgg,1094
4
+ square_authentication/messages.py,sha256=BA9KC0vW9UD1ZXT4VneVqVNLlgdbMdsAwAgxhJISLf4,1175
5
5
  square_authentication/data/config.ini,sha256=_740RvKpL5W2bUDGwZ7ePwuP-mAasr5cXXB81yq_Jv8,906
6
+ square_authentication/pydantic_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ square_authentication/pydantic_models/core.py,sha256=Bd6BPFPCV5PPA0KDKXUghIpoQA_w9MCSMY6ldSxM1qw,388
6
8
  square_authentication/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- square_authentication/routes/core.py,sha256=gwfNQcMvT5DdbZQBotLPwy1Actr7SBSgrimfHfMviYs,33617
9
+ square_authentication/routes/core.py,sha256=ijTmFDi8wg0oFycQRqxpXk4j4t6IqXq0FHffjecAnkQ,43303
8
10
  square_authentication/routes/utility.py,sha256=Kx4S4tZ1GKsPoC8CoZ4fkLEebvr02KeFEPePtTHtpnQ,75
9
11
  square_authentication/utils/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
12
  square_authentication/utils/encryption.py,sha256=T6BShoUr_xeGpbfPgTK-GxTlXPwcjwU4c4KW7KPzrF8,1865
11
13
  square_authentication/utils/token.py,sha256=Y_arg5LegX-aprMj9YweUK8jjNZLGDjLUGgxbUA12w4,560
12
- square_authentication-2.0.0.dist-info/METADATA,sha256=BrxsipcwD1dQNTUeM7pXXtAOZMPXBO1i4n7tRRbXx7s,1966
13
- square_authentication-2.0.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
14
- square_authentication-2.0.0.dist-info/top_level.txt,sha256=wDssVJIl9KIEJPj5rR3rv4uRI7yCndMBrvHd_6BGXQA,22
15
- square_authentication-2.0.0.dist-info/RECORD,,
14
+ square_authentication-3.0.0.dist-info/METADATA,sha256=6ahIoM0u8VAAGmp48nmRWwNMBj9tnN5X2q8oMr-G3Tc,2622
15
+ square_authentication-3.0.0.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
16
+ square_authentication-3.0.0.dist-info/top_level.txt,sha256=wDssVJIl9KIEJPj5rR3rv4uRI7yCndMBrvHd_6BGXQA,22
17
+ square_authentication-3.0.0.dist-info/RECORD,,