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 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
- async def get_verified_token(
56
- authorization: Annotated[HTTPAuthorizationCredentials, Depends(bearer_security)],
57
- ) -> TokenData | None:
58
- """
59
- Verify the JWT access token using OIDC authority JWKS.
60
- """
61
- global _JWKS
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
- if not oidc_settings.enabled:
64
- return None
70
+ token = authorization.credentials
65
71
 
66
- token = authorization.credentials
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
- try:
69
- if not _JWKS:
70
- openid_config = await fetch_openid_config()
71
- _JWKS = await fetch_jwks(openid_config['jwks_uri'])
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
- if oidc_settings.client_id:
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
- token_data = TokenData(**payload)
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
- return token_data
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 typing import Any
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
- return encode(data)
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
- def deserialize_msgpack_native(data: bytes, data_type=None):
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
- result = msgpack.decode(data, type=data_type)
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
- result = msgpack.decode(data, type=data_type)
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.31
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.2
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==3.4.0
26
- Requires-Dist: SQLAlchemy[asyncio]~=2.0.40
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=Fw2pudSkMAploLV2iOh_1LQalnijrO5kNCJfvA9cQfg,2485
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=5SS_wQXcrThPmCWjI7k1a_cHWLz46Jzn3pit89SYJGY,2038
24
- python3_commons-0.8.31.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
25
- python3_commons-0.8.31.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
26
- python3_commons-0.8.31.dist-info/METADATA,sha256=_-oT3jL2H0oalJS46wlfw84JNssr0Ymi396ukOmfZtU,1126
27
- python3_commons-0.8.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
- python3_commons-0.8.31.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
29
- python3_commons-0.8.31.dist-info/RECORD,,
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,,