square-authentication 6.0.5__py3-none-any.whl → 6.2.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.
- square_authentication/messages.py +1 -0
- square_authentication/routes/core.py +184 -0
- {square_authentication-6.0.5.dist-info → square_authentication-6.2.0.dist-info}/METADATA +11 -1
- {square_authentication-6.0.5.dist-info → square_authentication-6.2.0.dist-info}/RECORD +6 -6
- {square_authentication-6.0.5.dist-info → square_authentication-6.2.0.dist-info}/WHEEL +1 -1
- {square_authentication-6.0.5.dist-info → square_authentication-6.2.0.dist-info}/top_level.txt +0 -0
@@ -5,6 +5,7 @@ messages = {
|
|
5
5
|
"INCORRECT_USERNAME": "the username you entered does not exist.",
|
6
6
|
"INCORRECT_PASSWORD": "the password you entered is incorrect. please try again.",
|
7
7
|
"INCORRECT_USER_ID": "the user ID you provided does not exist or is invalid.",
|
8
|
+
"USERNAME_INVALID": "username must start and end with a lowercase letter and can include only lowercase letters, digits, underscores, or hyphens. no spaces, no dots, and no consecutive special characters.",
|
8
9
|
"USERNAME_ALREADY_EXISTS": "the username you entered is already taken. please choose a different one.",
|
9
10
|
"INCORRECT_ACCESS_TOKEN": "the access token provided is invalid or expired.",
|
10
11
|
"INCORRECT_REFRESH_TOKEN": "the refresh token provided is invalid or expired.",
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import copy
|
2
|
+
import re
|
2
3
|
from datetime import datetime, timedelta, timezone
|
3
4
|
from typing import Annotated, List
|
4
5
|
|
@@ -12,12 +13,14 @@ from square_commons import get_api_output_in_standard_format
|
|
12
13
|
from square_database_helper.pydantic_models import FilterConditionsV0, FiltersV0
|
13
14
|
from square_database_structure.square import global_string_database_name
|
14
15
|
from square_database_structure.square.authentication import global_string_schema_name
|
16
|
+
from square_database_structure.square.authentication.enums import RecoveryMethodEnum
|
15
17
|
from square_database_structure.square.authentication.tables import (
|
16
18
|
User,
|
17
19
|
UserApp,
|
18
20
|
UserCredential,
|
19
21
|
UserSession,
|
20
22
|
UserProfile,
|
23
|
+
UserRecoveryMethod,
|
21
24
|
)
|
22
25
|
from square_database_structure.square.public import (
|
23
26
|
global_string_schema_name as global_string_public_schema_name,
|
@@ -67,6 +70,22 @@ async def register_username_v0(
|
|
67
70
|
validation
|
68
71
|
"""
|
69
72
|
# validation for username
|
73
|
+
# ^(?!.*[_-]{2}) # no consecutive _ or -
|
74
|
+
# [a-z] # must start with a lowercase letter
|
75
|
+
# (?:[a-z0-9_-]{1,18}) # 1–18 of lowercase, digits, _ or -
|
76
|
+
# [a-z]$ # must end with a lowercase letter
|
77
|
+
username_pattern = re.compile(r"^(?!.*[._-]{2})[a-z][a-z0-9_-]{1,18}[a-z]$")
|
78
|
+
if not username_pattern.match(username):
|
79
|
+
output_content = get_api_output_in_standard_format(
|
80
|
+
message=messages["USERNAME_INVALID"],
|
81
|
+
log=f"username '{username}' is invalid. it must start and end with a letter, "
|
82
|
+
f"contain only lowercase letters, numbers, underscores, or hyphens, "
|
83
|
+
f"and not have consecutive separators.",
|
84
|
+
)
|
85
|
+
raise HTTPException(
|
86
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
87
|
+
detail=output_content,
|
88
|
+
)
|
70
89
|
local_list_response_user_creds = (
|
71
90
|
global_object_square_database_helper.get_rows_v0(
|
72
91
|
database_name=global_string_database_name,
|
@@ -1098,6 +1117,25 @@ async def update_username_v0(
|
|
1098
1117
|
)
|
1099
1118
|
user_id = local_dict_access_token_payload["user_id"]
|
1100
1119
|
|
1120
|
+
# validation for username
|
1121
|
+
# ^(?!.*[_-]{2}) # no consecutive _ or -
|
1122
|
+
# [a-z] # must start with a lowercase letter
|
1123
|
+
# (?:[a-z0-9_-]{1,18}) # 1–18 of lowercase, digits, _ or -
|
1124
|
+
# [a-z]$ # must end with a lowercase letter
|
1125
|
+
new_username = new_username.lower()
|
1126
|
+
username_pattern = re.compile(r"^(?!.*[._-]{2})[a-z][a-z0-9_-]{1,18}[a-z]$")
|
1127
|
+
if not username_pattern.match(new_username):
|
1128
|
+
output_content = get_api_output_in_standard_format(
|
1129
|
+
message=messages["USERNAME_INVALID"],
|
1130
|
+
log=f"username '{new_username}' is invalid. it must start and end with a letter, "
|
1131
|
+
f"contain only lowercase letters, numbers, underscores, or hyphens, "
|
1132
|
+
f"and not have consecutive separators.",
|
1133
|
+
)
|
1134
|
+
raise HTTPException(
|
1135
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1136
|
+
detail=output_content,
|
1137
|
+
)
|
1138
|
+
|
1101
1139
|
# validate user_id
|
1102
1140
|
local_list_user_response = global_object_square_database_helper.get_rows_v0(
|
1103
1141
|
database_name=global_string_database_name,
|
@@ -1504,3 +1542,149 @@ async def validate_and_get_payload_from_token_v0(
|
|
1504
1542
|
return JSONResponse(
|
1505
1543
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=output_content
|
1506
1544
|
)
|
1545
|
+
|
1546
|
+
|
1547
|
+
@router.patch("/update_user_recovery_methods/v0")
|
1548
|
+
@global_object_square_logger.auto_logger()
|
1549
|
+
async def update_user_recovery_methods_v0(
|
1550
|
+
access_token: Annotated[str, Header()],
|
1551
|
+
recovery_methods_to_add: List[RecoveryMethodEnum],
|
1552
|
+
recovery_methods_to_remove: List[RecoveryMethodEnum],
|
1553
|
+
):
|
1554
|
+
try:
|
1555
|
+
|
1556
|
+
"""
|
1557
|
+
validation
|
1558
|
+
"""
|
1559
|
+
# validate access token
|
1560
|
+
try:
|
1561
|
+
local_dict_access_token_payload = get_jwt_payload(
|
1562
|
+
access_token, config_str_secret_key_for_access_token
|
1563
|
+
)
|
1564
|
+
except Exception as error:
|
1565
|
+
output_content = get_api_output_in_standard_format(
|
1566
|
+
message=messages["INCORRECT_ACCESS_TOKEN"], log=str(error)
|
1567
|
+
)
|
1568
|
+
raise HTTPException(
|
1569
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1570
|
+
detail=output_content,
|
1571
|
+
)
|
1572
|
+
user_id = local_dict_access_token_payload["user_id"]
|
1573
|
+
|
1574
|
+
recovery_methods_to_add = list(set(x.value for x in recovery_methods_to_add))
|
1575
|
+
recovery_methods_to_remove = list(
|
1576
|
+
set(x.value for x in recovery_methods_to_remove)
|
1577
|
+
)
|
1578
|
+
|
1579
|
+
# check if recovery_methods_to_add and recovery_methods_to_remove don't have common ids.
|
1580
|
+
local_list_common_recovery_methods = set(recovery_methods_to_add) & set(
|
1581
|
+
recovery_methods_to_remove
|
1582
|
+
)
|
1583
|
+
if len(local_list_common_recovery_methods) > 0:
|
1584
|
+
output_content = get_api_output_in_standard_format(
|
1585
|
+
message=messages["GENERIC_400"],
|
1586
|
+
log=f"invalid recovery_methods: {list(local_list_common_recovery_methods)}, present in both add list and remove list.",
|
1587
|
+
)
|
1588
|
+
raise HTTPException(
|
1589
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
1590
|
+
detail=output_content,
|
1591
|
+
)
|
1592
|
+
|
1593
|
+
"""
|
1594
|
+
main process
|
1595
|
+
"""
|
1596
|
+
# logic for adding new recovery_methods
|
1597
|
+
local_list_response_user_recovery_methods = (
|
1598
|
+
global_object_square_database_helper.get_rows_v0(
|
1599
|
+
database_name=global_string_database_name,
|
1600
|
+
schema_name=global_string_schema_name,
|
1601
|
+
table_name=UserRecoveryMethod.__tablename__,
|
1602
|
+
filters=FiltersV0(
|
1603
|
+
root={
|
1604
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id)
|
1605
|
+
}
|
1606
|
+
),
|
1607
|
+
)["data"]["main"]
|
1608
|
+
)
|
1609
|
+
local_list_new_recovery_methods = [
|
1610
|
+
{
|
1611
|
+
UserRecoveryMethod.user_id.name: user_id,
|
1612
|
+
UserRecoveryMethod.user_recovery_method_name.name: x,
|
1613
|
+
}
|
1614
|
+
for x in recovery_methods_to_add
|
1615
|
+
if x
|
1616
|
+
not in [
|
1617
|
+
y[UserRecoveryMethod.user_recovery_method_name.name]
|
1618
|
+
for y in local_list_response_user_recovery_methods
|
1619
|
+
]
|
1620
|
+
]
|
1621
|
+
if len(local_list_new_recovery_methods) > 0:
|
1622
|
+
global_object_square_database_helper.insert_rows_v0(
|
1623
|
+
database_name=global_string_database_name,
|
1624
|
+
schema_name=global_string_schema_name,
|
1625
|
+
table_name=UserRecoveryMethod.__tablename__,
|
1626
|
+
data=local_list_new_recovery_methods,
|
1627
|
+
)
|
1628
|
+
|
1629
|
+
# logic for removing recovery_methods
|
1630
|
+
global_object_square_database_helper.delete_rows_v0(
|
1631
|
+
database_name=global_string_database_name,
|
1632
|
+
schema_name=global_string_schema_name,
|
1633
|
+
table_name=UserRecoveryMethod.__tablename__,
|
1634
|
+
filters=FiltersV0(
|
1635
|
+
root={
|
1636
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id),
|
1637
|
+
UserRecoveryMethod.user_recovery_method_name.name: FilterConditionsV0(
|
1638
|
+
in_=recovery_methods_to_remove
|
1639
|
+
),
|
1640
|
+
}
|
1641
|
+
),
|
1642
|
+
)
|
1643
|
+
|
1644
|
+
"""
|
1645
|
+
return value
|
1646
|
+
"""
|
1647
|
+
# get latest recovery_methods
|
1648
|
+
local_list_response_user_recovery_methods = (
|
1649
|
+
global_object_square_database_helper.get_rows_v0(
|
1650
|
+
database_name=global_string_database_name,
|
1651
|
+
schema_name=global_string_schema_name,
|
1652
|
+
table_name=UserRecoveryMethod.__tablename__,
|
1653
|
+
filters=FiltersV0(
|
1654
|
+
root={
|
1655
|
+
UserRecoveryMethod.user_id.name: FilterConditionsV0(eq=user_id)
|
1656
|
+
}
|
1657
|
+
),
|
1658
|
+
)["data"]["main"]
|
1659
|
+
)
|
1660
|
+
output_content = get_api_output_in_standard_format(
|
1661
|
+
message=messages["GENERIC_UPDATE_SUCCESSFUL"],
|
1662
|
+
data={
|
1663
|
+
"main": [
|
1664
|
+
x[UserRecoveryMethod.user_recovery_method_name.name]
|
1665
|
+
for x in local_list_response_user_recovery_methods
|
1666
|
+
]
|
1667
|
+
},
|
1668
|
+
)
|
1669
|
+
return JSONResponse(
|
1670
|
+
status_code=status.HTTP_200_OK,
|
1671
|
+
content=output_content,
|
1672
|
+
)
|
1673
|
+
except HTTPException as http_exception:
|
1674
|
+
global_object_square_logger.logger.error(http_exception, exc_info=True)
|
1675
|
+
return JSONResponse(
|
1676
|
+
status_code=http_exception.status_code, content=http_exception.detail
|
1677
|
+
)
|
1678
|
+
except Exception as e:
|
1679
|
+
"""
|
1680
|
+
rollback logic
|
1681
|
+
"""
|
1682
|
+
global_object_square_logger.logger.error(e, exc_info=True)
|
1683
|
+
output_content = get_api_output_in_standard_format(
|
1684
|
+
message=messages["GENERIC_500"],
|
1685
|
+
log=str(e),
|
1686
|
+
)
|
1687
|
+
return JSONResponse(
|
1688
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
1689
|
+
content=output_content,
|
1690
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: square_authentication
|
3
|
-
Version: 6.0
|
3
|
+
Version: 6.2.0
|
4
4
|
Summary: authentication layer for my personal server.
|
5
5
|
Home-page: https://github.com/thepmsquare/square_authentication
|
6
6
|
Author: thePmSquare
|
@@ -53,6 +53,16 @@ pip install square_authentication
|
|
53
53
|
|
54
54
|
## changelog
|
55
55
|
|
56
|
+
### v6.2.0
|
57
|
+
|
58
|
+
- core
|
59
|
+
- add update_user_recovery_methods_v0.
|
60
|
+
|
61
|
+
### v6.1.0
|
62
|
+
|
63
|
+
- add validation to username in register_username_v0 and update_username_v0.
|
64
|
+
- add test cases for register_username_v0.
|
65
|
+
|
56
66
|
### v6.0.5
|
57
67
|
|
58
68
|
- env
|
@@ -1,19 +1,19 @@
|
|
1
1
|
square_authentication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
square_authentication/configuration.py,sha256=zdlBY7xyMoEl8fc2whpXqsILvvrNnFGC1Yry_pB6FaI,4579
|
3
3
|
square_authentication/main.py,sha256=nhkv8U4E9b7VIH7Aaj8iMWIwA4VIL-vzRXjZaYEFWPw,1755
|
4
|
-
square_authentication/messages.py,sha256=
|
4
|
+
square_authentication/messages.py,sha256=WVZtWBctx-YK1xGo97DFAMkSgCZpDEd9gABBSqlvd58,1575
|
5
5
|
square_authentication/data/config.ini,sha256=Mayh9AhTBZd8i08Y--ClZuDEjJjfvnfKQmtqablzXOA,1154
|
6
6
|
square_authentication/data/config.testing.ini,sha256=KB4PMPZ6a9yJGPXYJWwKlYcNET1Au3TQdJHQyngbZUA,1201
|
7
7
|
square_authentication/pydantic_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
square_authentication/pydantic_models/core.py,sha256=qeNETcJv7mnRKGhATOW2bg0NlHuyzvot1dZ1b1qqhwU,610
|
9
9
|
square_authentication/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
square_authentication/routes/core.py,sha256=
|
10
|
+
square_authentication/routes/core.py,sha256=ibkNyYIDTt20P9I-H-Y9674vGHzeRIqWNnGr3Ow2VS4,64432
|
11
11
|
square_authentication/routes/profile.py,sha256=3b-PtMaD9cxvf112MOn9rPu5F2KG4sRxAbuPvll8dUU,6216
|
12
12
|
square_authentication/routes/utility.py,sha256=KDr8KdkT0jAGPjfP-b5XXYG7p49WU7J1FiK6oSIckQI,1779
|
13
13
|
square_authentication/utils/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
14
14
|
square_authentication/utils/encryption.py,sha256=WakaiEAgWpTJltxBzqOtv81_DCDKfzJqt60fWSPoNvo,2027
|
15
15
|
square_authentication/utils/token.py,sha256=t-RPBY4cYyT1ro3lkLBTOy2BeRGBfluBVBivL5DLmDg,680
|
16
|
-
square_authentication-6.0.
|
17
|
-
square_authentication-6.0.
|
18
|
-
square_authentication-6.0.
|
19
|
-
square_authentication-6.0.
|
16
|
+
square_authentication-6.2.0.dist-info/METADATA,sha256=37LOBkSBmItsK4nqZ1MsXdZMisbREl29U7Klz9OedR4,5397
|
17
|
+
square_authentication-6.2.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
18
|
+
square_authentication-6.2.0.dist-info/top_level.txt,sha256=wDssVJIl9KIEJPj5rR3rv4uRI7yCndMBrvHd_6BGXQA,22
|
19
|
+
square_authentication-6.2.0.dist-info/RECORD,,
|
{square_authentication-6.0.5.dist-info → square_authentication-6.2.0.dist-info}/top_level.txt
RENAMED
File without changes
|