python3-commons 0.8.31__py3-none-any.whl → 0.8.33__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.
- python3_commons/auth.py +31 -23
- python3_commons/serializers/msgspec.py +27 -9
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/METADATA +4 -4
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/RECORD +8 -8
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/WHEEL +0 -0
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/licenses/AUTHORS.rst +0 -0
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/licenses/LICENSE +0 -0
- {python3_commons-0.8.31.dist-info → python3_commons-0.8.33.dist-info}/top_level.txt +0 -0
python3_commons/auth.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
from http import HTTPStatus
|
3
|
-
from typing import Annotated, Sequence
|
3
|
+
from typing import Annotated, Any, Callable, Coroutine, Sequence, Type, TypeVar
|
4
4
|
|
5
5
|
import aiohttp
|
6
6
|
from fastapi import Depends, HTTPException
|
@@ -20,6 +20,9 @@ class TokenData(BaseModel):
|
|
20
20
|
iss: str
|
21
21
|
|
22
22
|
|
23
|
+
T = TypeVar('T', bound=TokenData)
|
24
|
+
|
25
|
+
|
23
26
|
OIDC_CONFIG_URL = f'{oidc_settings.authority_url}/.well-known/openid-configuration'
|
24
27
|
_JWKS: dict | None = None
|
25
28
|
|
@@ -52,31 +55,36 @@ async def fetch_jwks(jwks_uri: str) -> dict:
|
|
52
55
|
return await response.json()
|
53
56
|
|
54
57
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
def get_token_verifier(token_cls: Type[T]) -> Callable[[HTTPAuthorizationCredentials], Coroutine[Any, Any, T | None]]:
|
59
|
+
async def get_verified_token(
|
60
|
+
authorization: Annotated[HTTPAuthorizationCredentials, Depends(bearer_security)],
|
61
|
+
) -> T | None:
|
62
|
+
"""
|
63
|
+
Verify the JWT access token using OIDC authority JWKS.
|
64
|
+
"""
|
65
|
+
global _JWKS
|
66
|
+
|
67
|
+
if not oidc_settings.enabled:
|
68
|
+
return None
|
62
69
|
|
63
|
-
|
64
|
-
return None
|
70
|
+
token = authorization.credentials
|
65
71
|
|
66
|
-
|
72
|
+
try:
|
73
|
+
if not _JWKS:
|
74
|
+
openid_config = await fetch_openid_config()
|
75
|
+
_JWKS = await fetch_jwks(openid_config['jwks_uri'])
|
67
76
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
77
|
+
if oidc_settings.client_id:
|
78
|
+
payload = jwt.decode(token, _JWKS, algorithms=['RS256'], audience=oidc_settings.client_id)
|
79
|
+
else:
|
80
|
+
payload = jwt.decode(token, _JWKS, algorithms=['RS256'])
|
72
81
|
|
73
|
-
|
74
|
-
payload = jwt.decode(token, _JWKS, algorithms=['RS256'], audience=oidc_settings.client_id)
|
75
|
-
else:
|
76
|
-
payload = jwt.decode(token, _JWKS, algorithms=['RS256'])
|
82
|
+
token_data = token_cls(**payload)
|
77
83
|
|
78
|
-
|
84
|
+
return token_data
|
85
|
+
except jwt.ExpiredSignatureError:
|
86
|
+
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail='Token has expired')
|
87
|
+
except JWTError as e:
|
88
|
+
raise HTTPException(status_code=HTTPStatus.UNAUTHORIZED, detail=f'Token is invalid: {str(e)}')
|
79
89
|
|
80
|
-
|
81
|
-
except JWTError as e:
|
82
|
-
raise HTTPException(status_code=HTTPStatus.FORBIDDEN, detail=f'Token is invalid: {str(e)}')
|
90
|
+
return get_verified_token
|
@@ -2,16 +2,18 @@ import dataclasses
|
|
2
2
|
import json
|
3
3
|
import logging
|
4
4
|
import struct
|
5
|
-
from _decimal import Decimal
|
6
5
|
from datetime import date, datetime
|
7
|
-
from
|
6
|
+
from decimal import Decimal
|
7
|
+
from typing import Any, Type, TypeVar
|
8
8
|
|
9
9
|
from msgspec import msgpack
|
10
10
|
from msgspec.msgpack import Ext, encode
|
11
|
+
from pydantic import BaseModel
|
11
12
|
|
12
13
|
from python3_commons.serializers.json import CustomJSONEncoder
|
13
14
|
|
14
15
|
logger = logging.getLogger(__name__)
|
16
|
+
T = TypeVar('T')
|
15
17
|
|
16
18
|
|
17
19
|
def enc_hook(obj: Any) -> Any:
|
@@ -45,28 +47,44 @@ MSGPACK_DECODER = msgpack.Decoder(ext_hook=ext_hook)
|
|
45
47
|
MSGPACK_DECODER_NATIVE = msgpack.Decoder()
|
46
48
|
|
47
49
|
|
48
|
-
def serialize_msgpack_native(data) -> bytes:
|
49
|
-
|
50
|
+
def serialize_msgpack_native(data: Any) -> bytes:
|
51
|
+
if isinstance(data, BaseModel):
|
52
|
+
data = data.model_dump()
|
50
53
|
|
54
|
+
result = encode(data)
|
51
55
|
|
52
|
-
|
56
|
+
return result
|
57
|
+
|
58
|
+
|
59
|
+
def deserialize_msgpack_native(data: bytes, data_type: Type[T] | None = None) -> T | Any:
|
53
60
|
if data_type:
|
54
|
-
|
61
|
+
if issubclass(data_type, BaseModel):
|
62
|
+
decoded = MSGPACK_DECODER_NATIVE.decode(data)
|
63
|
+
result = data_type.model_validate(decoded)
|
64
|
+
else:
|
65
|
+
result = msgpack.decode(data, type=data_type)
|
55
66
|
else:
|
56
67
|
result = MSGPACK_DECODER_NATIVE.decode(data)
|
57
68
|
|
58
69
|
return result
|
59
70
|
|
60
71
|
|
61
|
-
def serialize_msgpack(data) -> bytes:
|
72
|
+
def serialize_msgpack(data: Any) -> bytes:
|
73
|
+
if isinstance(data, BaseModel):
|
74
|
+
data = data.model_dump()
|
75
|
+
|
62
76
|
result = MSGPACK_ENCODER.encode(data)
|
63
77
|
|
64
78
|
return result
|
65
79
|
|
66
80
|
|
67
|
-
def deserialize_msgpack(data: bytes, data_type=None):
|
81
|
+
def deserialize_msgpack(data: bytes, data_type: Type[T] | None = None) -> T | Any:
|
68
82
|
if data_type:
|
69
|
-
|
83
|
+
if issubclass(data_type, BaseModel):
|
84
|
+
decoded = MSGPACK_DECODER.decode(data)
|
85
|
+
result = data_type.model_validate(decoded)
|
86
|
+
else:
|
87
|
+
result = msgpack.decode(data, type=data_type)
|
70
88
|
else:
|
71
89
|
result = MSGPACK_DECODER.decode(data)
|
72
90
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: python3-commons
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.33
|
4
4
|
Summary: Re-usable Python3 code
|
5
5
|
Author-email: Oleg Korsak <kamikaze.is.waiting.you@gmail.com>
|
6
6
|
License-Expression: GPL-3.0
|
@@ -12,7 +12,7 @@ Requires-Python: ==3.13.*
|
|
12
12
|
Description-Content-Type: text/x-rst
|
13
13
|
License-File: LICENSE
|
14
14
|
License-File: AUTHORS.rst
|
15
|
-
Requires-Dist: aiohttp[speedups]~=3.12.
|
15
|
+
Requires-Dist: aiohttp[speedups]~=3.12.4
|
16
16
|
Requires-Dist: asyncpg~=0.30.0
|
17
17
|
Requires-Dist: fastapi-users-db-sqlalchemy~=7.0.0
|
18
18
|
Requires-Dist: fastapi-users[sqlalchemy]~=14.0.1
|
@@ -22,8 +22,8 @@ Requires-Dist: msgpack~=1.1.0
|
|
22
22
|
Requires-Dist: msgspec~=0.19.0
|
23
23
|
Requires-Dist: pydantic[email]~=2.11.5
|
24
24
|
Requires-Dist: pydantic-settings~=2.9.1
|
25
|
-
Requires-Dist: python-jose
|
26
|
-
Requires-Dist: SQLAlchemy[asyncio]~=2.0.
|
25
|
+
Requires-Dist: python-jose~=3.5.0
|
26
|
+
Requires-Dist: SQLAlchemy[asyncio]~=2.0.41
|
27
27
|
Requires-Dist: valkey[libvalkey]~=6.1.0
|
28
28
|
Requires-Dist: zeep~=4.3.1
|
29
29
|
Dynamic: license-file
|
@@ -1,7 +1,7 @@
|
|
1
1
|
python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,189
|
2
2
|
python3_commons/api_client.py,sha256=LT7_YmnYVHK2ucKxIhUJCZrmxgfy-lfOxx08-R0WvW0,4505
|
3
3
|
python3_commons/audit.py,sha256=osx2ywZXf-V0zOkrhlNgSyzCBvojXQwSYBQ4-ze1xiM,6249
|
4
|
-
python3_commons/auth.py,sha256=
|
4
|
+
python3_commons/auth.py,sha256=vVaiJ5MHUMSbiLF6TIxe4dqVPhBlLttf940jjODL3a4,2934
|
5
5
|
python3_commons/cache.py,sha256=lf27LTD4Z9Iqi5GaK8jH8UC0cL9sHH8wicZ88YDp6Mg,7725
|
6
6
|
python3_commons/conf.py,sha256=JenspXyTqTlYeEb8X9Njfje1AiLCee23nm0k8zhYLfs,2283
|
7
7
|
python3_commons/fs.py,sha256=wfLjybXndwLqNlOxTpm_HRJnuTcC4wbrHEOaEeCo9Wc,337
|
@@ -20,10 +20,10 @@ python3_commons/log/formatters.py,sha256=p2AtZD4Axp3Em0e9gWzW8U_yOR5entD7xn7Edvc
|
|
20
20
|
python3_commons/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
21
|
python3_commons/serializers/json.py,sha256=91UaXLGKGj0yPyrnuMeNrkG2GuPUgcgAsmIokUgEwpU,808
|
22
22
|
python3_commons/serializers/msgpack.py,sha256=WrvaPE187shSK8zkH4UHHMimEZNMv9RaDSwsBE2HlCw,1269
|
23
|
-
python3_commons/serializers/msgspec.py,sha256=
|
24
|
-
python3_commons-0.8.
|
25
|
-
python3_commons-0.8.
|
26
|
-
python3_commons-0.8.
|
27
|
-
python3_commons-0.8.
|
28
|
-
python3_commons-0.8.
|
29
|
-
python3_commons-0.8.
|
23
|
+
python3_commons/serializers/msgspec.py,sha256=0AliXlEl5sewi0UENjI8St5ZScXE5DNRERKzqWKy2Ps,2674
|
24
|
+
python3_commons-0.8.33.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
|
25
|
+
python3_commons-0.8.33.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
|
26
|
+
python3_commons-0.8.33.dist-info/METADATA,sha256=UZfV_4KMnNb0AjyRDYJA19stTPikKfm_jxiO5LMlc5E,1126
|
27
|
+
python3_commons-0.8.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
28
|
+
python3_commons-0.8.33.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
|
29
|
+
python3_commons-0.8.33.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|