python3-commons 0.22.6__py3-none-any.whl → 0.22.7__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 +44 -24
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/METADATA +1 -1
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/RECORD +9 -9
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/WHEEL +1 -1
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/scm_file_list.json +45 -44
- python3_commons-0.22.7.dist-info/scm_version.json +8 -0
- python3_commons-0.22.6.dist-info/scm_version.json +0 -8
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/licenses/AUTHORS.rst +0 -0
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/licenses/LICENSE +0 -0
- {python3_commons-0.22.6.dist-info → python3_commons-0.22.7.dist-info}/top_level.txt +0 -0
python3_commons/auth.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import threading
|
|
3
|
-
from collections.abc import
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from time import monotonic
|
|
4
5
|
from typing import Any, Self, TypeVar
|
|
5
6
|
|
|
6
7
|
from pydantic import HttpUrl
|
|
@@ -18,9 +19,8 @@ except ImportError as e:
|
|
|
18
19
|
import msgspec
|
|
19
20
|
|
|
20
21
|
logger = logging.getLogger(__name__)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
_OIDC_SESSION_LOCK = threading.Lock()
|
|
22
|
+
|
|
23
|
+
DEFAULT_JWKS_CACHE_TTL = 300.0
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class TokenData(msgspec.Struct):
|
|
@@ -81,6 +81,7 @@ class OIDCClient:
|
|
|
81
81
|
timeout: float = 10.0,
|
|
82
82
|
verify_cert: bool = True,
|
|
83
83
|
connection_limit: int = 100,
|
|
84
|
+
jwks_cache_ttl: float = DEFAULT_JWKS_CACHE_TTL,
|
|
84
85
|
authority_internal_host: HttpUrl | None = None,
|
|
85
86
|
audit_name: str | None = None,
|
|
86
87
|
) -> None:
|
|
@@ -97,17 +98,22 @@ class OIDCClient:
|
|
|
97
98
|
self._timeout = timeout
|
|
98
99
|
self._verify_cert = verify_cert
|
|
99
100
|
|
|
100
|
-
self.
|
|
101
|
-
self.
|
|
101
|
+
self._session_lock = threading.Lock()
|
|
102
|
+
self._config: dict[str, Any] | None = None
|
|
103
|
+
self._config_lock = threading.Lock()
|
|
104
|
+
self._jwks: dict[str, Any] | None = None
|
|
105
|
+
self._jwks_lock = threading.Lock()
|
|
106
|
+
self._jwks_cache_ttl = jwks_cache_ttl
|
|
107
|
+
self._jwks_fetched_at: float | None = None
|
|
102
108
|
self._audit_name: str | None = audit_name
|
|
103
109
|
|
|
104
110
|
def _get_session(self) -> aiohttp.ClientSession:
|
|
105
|
-
if self._session:
|
|
106
|
-
return
|
|
111
|
+
if (session := self._session) and not session.closed:
|
|
112
|
+
return session
|
|
107
113
|
|
|
108
|
-
with
|
|
109
|
-
if self._session:
|
|
110
|
-
return
|
|
114
|
+
with self._session_lock:
|
|
115
|
+
if (session := self._session) and not session.closed:
|
|
116
|
+
return session
|
|
111
117
|
|
|
112
118
|
connector = aiohttp.TCPConnector(verify_ssl=self._verify_cert, limit=self._connection_limit)
|
|
113
119
|
timeout = aiohttp.ClientTimeout(total=self._timeout)
|
|
@@ -137,20 +143,20 @@ class OIDCClient:
|
|
|
137
143
|
) as response:
|
|
138
144
|
return await response.json()
|
|
139
145
|
|
|
140
|
-
async def get_config(self) ->
|
|
141
|
-
if self._config:
|
|
142
|
-
return
|
|
146
|
+
async def get_config(self) -> dict[str, Any]:
|
|
147
|
+
if config := self._config:
|
|
148
|
+
return config
|
|
143
149
|
|
|
144
|
-
with
|
|
145
|
-
if self._config:
|
|
146
|
-
return
|
|
150
|
+
with self._config_lock:
|
|
151
|
+
if config := self._config:
|
|
152
|
+
return config
|
|
147
153
|
|
|
148
154
|
config = await self._fetch_config()
|
|
149
155
|
self._config = config
|
|
150
156
|
|
|
151
157
|
return config
|
|
152
158
|
|
|
153
|
-
async def _fetch_jwks(self, jwks_uri: str) -> dict:
|
|
159
|
+
async def _fetch_jwks(self, jwks_uri: str) -> dict[str, Any]:
|
|
154
160
|
"""
|
|
155
161
|
Fetch the JSON Web Key Set (JWKS) for validating the token's signature.
|
|
156
162
|
"""
|
|
@@ -163,18 +169,32 @@ class OIDCClient:
|
|
|
163
169
|
async with api_client.request(self._get_session(), jwks_uri, '', audit_name=self._audit_name) as response:
|
|
164
170
|
return await response.json()
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
172
|
+
def _is_fresh_jwks(self) -> bool:
|
|
173
|
+
fetched_at = self._jwks_fetched_at
|
|
174
|
+
|
|
175
|
+
if self._jwks is None or fetched_at is None:
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
return (monotonic() - fetched_at) < self._jwks_cache_ttl
|
|
179
|
+
|
|
180
|
+
async def get_jwks(self, *, force_refresh: bool = False) -> dict[str, Any]:
|
|
181
|
+
if not force_refresh and (jwks := self._jwks) and self._is_fresh_jwks():
|
|
182
|
+
return jwks
|
|
183
|
+
|
|
184
|
+
fetched_at = self._jwks_fetched_at
|
|
185
|
+
|
|
186
|
+
with self._jwks_lock:
|
|
187
|
+
if (jwks := self._jwks) and self._jwks_fetched_at != fetched_at:
|
|
188
|
+
return jwks
|
|
169
189
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return self._jwks
|
|
190
|
+
if not force_refresh and jwks and self._is_fresh_jwks():
|
|
191
|
+
return jwks
|
|
173
192
|
|
|
174
193
|
oidc_config = await self.get_config()
|
|
175
194
|
|
|
176
195
|
jwks = await self._fetch_jwks(oidc_config['jwks_uri'])
|
|
177
196
|
self._jwks = jwks
|
|
197
|
+
self._jwks_fetched_at = monotonic()
|
|
178
198
|
|
|
179
199
|
return jwks
|
|
180
200
|
|
|
@@ -2,7 +2,7 @@ python3_commons/__init__.py,sha256=0KgaYU46H_IMKn-BuasoRN3C4Hi45KlkHHoPbU9cwiA,1
|
|
|
2
2
|
python3_commons/api_client.py,sha256=SThSzLhZJxVsKP8n1xDMCczA7SBFbxGFgrhKy9k-9_g,5532
|
|
3
3
|
python3_commons/async_functools.py,sha256=A2HvwFzZHxOWTp4IQM5UiBY2yg1S_0U1CWra5BWK0gk,9101
|
|
4
4
|
python3_commons/audit.py,sha256=uGoCwenDJ0Gdwbr_VNOZm5scT8luxW1weprJbbMoHo0,2608
|
|
5
|
-
python3_commons/auth.py,sha256=
|
|
5
|
+
python3_commons/auth.py,sha256=9gRdXe54O9e86DS8XgXlX9QDCqrhPIZLJaa4AC9RR_0,8239
|
|
6
6
|
python3_commons/cache.py,sha256=lowiXJqFgFy1Yg86wi9IhuoNqIUGP6nc5eNibmf0dfY,8018
|
|
7
7
|
python3_commons/conf.py,sha256=1HYtNkNBQc5IIABzhH3kz7GnLeF7rj5aNcOoW8xbEa4,3101
|
|
8
8
|
python3_commons/exceptions.py,sha256=EGjHZVBnsM6CeBfPMqhL0IPMKjDJ_2-Z-aSPXwq91LE,36
|
|
@@ -27,11 +27,11 @@ python3_commons/serializers/common.py,sha256=VkA7C6wODvHk0QBXVX_x2JieDstihx3U__U
|
|
|
27
27
|
python3_commons/serializers/json.py,sha256=UPkC3ps13x2C_NxwVV-K7Ewp4VjkVHSSUkJVw5k7Wiw,712
|
|
28
28
|
python3_commons/serializers/msgpack.py,sha256=zESFBX34GsZ8rDu6Zk5V6CLT6P0mPilU0r04Ka6TblI,1474
|
|
29
29
|
python3_commons/serializers/msgspec.py,sha256=upy5CBmK66-8hYnK5bAM_sZvZY5CAqZmzCw9GIF346I,2988
|
|
30
|
-
python3_commons-0.22.
|
|
31
|
-
python3_commons-0.22.
|
|
32
|
-
python3_commons-0.22.
|
|
33
|
-
python3_commons-0.22.
|
|
34
|
-
python3_commons-0.22.
|
|
35
|
-
python3_commons-0.22.
|
|
36
|
-
python3_commons-0.22.
|
|
37
|
-
python3_commons-0.22.
|
|
30
|
+
python3_commons-0.22.7.dist-info/licenses/AUTHORS.rst,sha256=3R9JnfjfjH5RoPWOeqKFJgxVShSSfzQPIrEr1nxIo9Q,90
|
|
31
|
+
python3_commons-0.22.7.dist-info/licenses/LICENSE,sha256=xxILuojHm4fKQOrMHPSslbyy6WuKAN2RiG74HbrYfzM,34575
|
|
32
|
+
python3_commons-0.22.7.dist-info/METADATA,sha256=QHKCW7uvvkYZHybS_GrgR3t1kSKr3Zdp4oIqN1UuLgs,9472
|
|
33
|
+
python3_commons-0.22.7.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
34
|
+
python3_commons-0.22.7.dist-info/scm_file_list.json,sha256=_VHernCbTfExHtROxsobMxaHoIS1TJJ6S-8WTB3Aqvs,2549
|
|
35
|
+
python3_commons-0.22.7.dist-info/scm_version.json,sha256=Flp3XnyTjvM7r12ci_e2s_UtGH9o8RT-MNUA8D2WUWA,161
|
|
36
|
+
python3_commons-0.22.7.dist-info/top_level.txt,sha256=lJI6sCBf68eUHzupCnn2dzG10lH3jJKTWM_hrN1cQ7M,16
|
|
37
|
+
python3_commons-0.22.7.dist-info/RECORD,,
|
|
@@ -1,75 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
+
".gitignore",
|
|
4
|
+
".python-version",
|
|
5
|
+
"README.md",
|
|
6
|
+
"AUTHORS.rst",
|
|
7
|
+
"CHANGELOG.rst",
|
|
8
|
+
"pyproject.toml",
|
|
9
|
+
".env_template",
|
|
3
10
|
".coveragerc",
|
|
4
11
|
".pre-commit-config.yaml",
|
|
5
|
-
"README.md",
|
|
6
12
|
"uv.lock",
|
|
7
|
-
".python-version",
|
|
8
13
|
"LICENSE",
|
|
9
|
-
"pyproject.toml",
|
|
10
|
-
"CHANGELOG.rst",
|
|
11
|
-
".env_template",
|
|
12
14
|
"README.rst",
|
|
13
|
-
".
|
|
14
|
-
"AUTHORS.rst",
|
|
15
|
-
".devcontainer/Dockerfile",
|
|
16
|
-
".devcontainer/docker-compose.yml",
|
|
17
|
-
".devcontainer/devcontainer.json",
|
|
18
|
-
"docs/Makefile",
|
|
19
|
-
"docs/license.rst",
|
|
20
|
-
"docs/authors.rst",
|
|
21
|
-
"docs/index.rst",
|
|
22
|
-
"docs/changelog.rst",
|
|
23
|
-
"docs/conf.py",
|
|
24
|
-
"docs/_static/.gitignore",
|
|
25
|
-
"src/python3_commons/helpers.py",
|
|
15
|
+
"src/python3_commons/conf.py",
|
|
26
16
|
"src/python3_commons/soap_client.py",
|
|
27
|
-
"src/python3_commons/
|
|
28
|
-
"src/python3_commons/__init__.py",
|
|
29
|
-
"src/python3_commons/exceptions.py",
|
|
30
|
-
"src/python3_commons/object_storage.py",
|
|
17
|
+
"src/python3_commons/async_functools.py",
|
|
31
18
|
"src/python3_commons/permissions.py",
|
|
32
|
-
"src/python3_commons/auth.py",
|
|
33
|
-
"src/python3_commons/audit.py",
|
|
34
|
-
"src/python3_commons/cache.py",
|
|
35
19
|
"src/python3_commons/fs.py",
|
|
36
|
-
"src/python3_commons/
|
|
37
|
-
"src/python3_commons/
|
|
20
|
+
"src/python3_commons/api_client.py",
|
|
21
|
+
"src/python3_commons/audit.py",
|
|
22
|
+
"src/python3_commons/object_storage.py",
|
|
23
|
+
"src/python3_commons/exceptions.py",
|
|
24
|
+
"src/python3_commons/helpers.py",
|
|
38
25
|
"src/python3_commons/generators.py",
|
|
26
|
+
"src/python3_commons/cache.py",
|
|
27
|
+
"src/python3_commons/auth.py",
|
|
28
|
+
"src/python3_commons/__init__.py",
|
|
39
29
|
"src/python3_commons/db/helpers.py",
|
|
40
30
|
"src/python3_commons/db/__init__.py",
|
|
31
|
+
"src/python3_commons/db/models/common.py",
|
|
41
32
|
"src/python3_commons/db/models/rbac.py",
|
|
42
|
-
"src/python3_commons/db/models/__init__.py",
|
|
43
|
-
"src/python3_commons/db/models/users.py",
|
|
44
33
|
"src/python3_commons/db/models/auth.py",
|
|
45
|
-
"src/python3_commons/db/models/
|
|
46
|
-
"src/python3_commons/
|
|
34
|
+
"src/python3_commons/db/models/users.py",
|
|
35
|
+
"src/python3_commons/db/models/__init__.py",
|
|
47
36
|
"src/python3_commons/log/filters.py",
|
|
48
37
|
"src/python3_commons/log/formatters.py",
|
|
49
|
-
"src/python3_commons/
|
|
50
|
-
"src/python3_commons/serializers/msgpack.py",
|
|
51
|
-
"src/python3_commons/serializers/json.py",
|
|
38
|
+
"src/python3_commons/log/__init__.py",
|
|
52
39
|
"src/python3_commons/serializers/msgspec.py",
|
|
40
|
+
"src/python3_commons/serializers/json.py",
|
|
53
41
|
"src/python3_commons/serializers/common.py",
|
|
42
|
+
"src/python3_commons/serializers/msgpack.py",
|
|
43
|
+
"src/python3_commons/serializers/__init__.py",
|
|
44
|
+
".devcontainer/docker-compose.yml",
|
|
45
|
+
".devcontainer/Dockerfile",
|
|
46
|
+
".devcontainer/devcontainer.json",
|
|
47
|
+
".github/workflows/checks.yml",
|
|
48
|
+
".github/workflows/release-on-tag-push.yml",
|
|
49
|
+
".github/workflows/python-publish.yaml",
|
|
54
50
|
"tests/__init__.py",
|
|
55
|
-
"tests/integration/__init__.py",
|
|
56
|
-
"tests/integration/test_auth.py",
|
|
57
51
|
"tests/integration/conftest.py",
|
|
58
52
|
"tests/integration/test_osc.py",
|
|
59
53
|
"tests/integration/test_cache.py",
|
|
60
|
-
"tests/
|
|
61
|
-
"tests/
|
|
54
|
+
"tests/integration/test_auth.py",
|
|
55
|
+
"tests/integration/__init__.py",
|
|
62
56
|
"tests/unit/test_async_functools.py",
|
|
63
|
-
"tests/unit/test_msgspec.py",
|
|
64
57
|
"tests/unit/conftest.py",
|
|
58
|
+
"tests/unit/test_msgspec.py",
|
|
65
59
|
"tests/unit/test_audit.py",
|
|
60
|
+
"tests/unit/test_auth.py",
|
|
66
61
|
"tests/unit/test_msgpack.py",
|
|
67
|
-
"tests/unit/
|
|
62
|
+
"tests/unit/test_helpers.py",
|
|
63
|
+
"tests/unit/__init__.py",
|
|
68
64
|
"tests/unit/db/test_async_session_manager.py",
|
|
69
|
-
"tests/unit/
|
|
65
|
+
"tests/unit/db/__init__.py",
|
|
70
66
|
"tests/unit/log/test_formatters.py",
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
67
|
+
"tests/unit/log/__init__.py",
|
|
68
|
+
"docs/conf.py",
|
|
69
|
+
"docs/Makefile",
|
|
70
|
+
"docs/index.rst",
|
|
71
|
+
"docs/authors.rst",
|
|
72
|
+
"docs/changelog.rst",
|
|
73
|
+
"docs/license.rst",
|
|
74
|
+
"docs/_static/.gitignore"
|
|
74
75
|
]
|
|
75
76
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|