pypomes-jwt 0.5.7__tar.gz → 0.5.9__tar.gz
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.
Potentially problematic release.
This version of pypomes-jwt might be problematic. Click here for more details.
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/PKG-INFO +1 -1
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/pyproject.toml +1 -1
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/src/pypomes_jwt/jwt_data.py +101 -64
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/src/pypomes_jwt/jwt_pomes.py +72 -34
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/.gitignore +0 -0
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/LICENSE +0 -0
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/README.md +0 -0
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/src/__init__.py +0 -0
- {pypomes_jwt-0.5.7 → pypomes_jwt-0.5.9}/src/pypomes_jwt/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_jwt
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.9
|
|
4
4
|
Summary: A collection of Python pomes, penyeach (JWT module)
|
|
5
5
|
Project-URL: Homepage, https://github.com/TheWiseCoder/PyPomes-JWT
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/TheWiseCoder/PyPomes-JWT/issues
|
|
@@ -36,8 +36,8 @@ class JwtData:
|
|
|
36
36
|
"request-timeout": <float>, # in seconds - defaults to no timeout
|
|
37
37
|
"access-max-age": <int>, # in seconds - defaults to JWT_ACCESS_MAX_AGE
|
|
38
38
|
"refresh-exp": <timestamp>, # expiration time for the refresh operation
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"reference-url": <url>, # URL to obtain and validate the access tokens
|
|
40
|
+
"remote-provider": <bool>, # whether the JWT provider is a remote server
|
|
41
41
|
"secret-key": <bytes>, # HS secret key
|
|
42
42
|
"private-key": <bytes>, # RSA private key
|
|
43
43
|
"public-key": <bytes>, # RSA public key
|
|
@@ -54,7 +54,7 @@ class JwtData:
|
|
|
54
54
|
self.access_data: list[dict[str, dict[str, Any]]] = []
|
|
55
55
|
|
|
56
56
|
def add_access_data(self,
|
|
57
|
-
|
|
57
|
+
reference_url: str,
|
|
58
58
|
claims: dict[str, Any],
|
|
59
59
|
algorithm: Literal["HS256", "HS512", "RSA256", "RSA512"],
|
|
60
60
|
access_max_age: int,
|
|
@@ -63,15 +63,16 @@ class JwtData:
|
|
|
63
63
|
private_key: bytes,
|
|
64
64
|
public_key: bytes,
|
|
65
65
|
request_timeout: float,
|
|
66
|
+
remote_provider: bool,
|
|
66
67
|
logger: Logger = None) -> None:
|
|
67
68
|
"""
|
|
68
|
-
Add to storage the parameters needed to obtain and validate JWT tokens
|
|
69
|
+
Add to storage the parameters needed to obtain and validate JWT tokens for *reference_url*.
|
|
69
70
|
|
|
70
71
|
Presently, the *refresh_max_age* data is not relevant, as the authorization parameters in *claims*
|
|
71
72
|
(typically, an acess-key/secret-key pair), have been previously validated elsewhere.
|
|
72
73
|
This situation might change in the future.
|
|
73
74
|
|
|
74
|
-
:param
|
|
75
|
+
:param reference_url: the reference URL
|
|
75
76
|
:param claims: the JWT claimset, as key-value pairs
|
|
76
77
|
:param algorithm: the algorithm used to sign the token with
|
|
77
78
|
:param access_max_age: token duration
|
|
@@ -80,18 +81,18 @@ class JwtData:
|
|
|
80
81
|
:param private_key: private key for RSA authentication
|
|
81
82
|
:param public_key: public key for RSA authentication
|
|
82
83
|
:param request_timeout: timeout for the requests to the service URL
|
|
84
|
+
:param remote_provider: whether the JWT provider is a remote server
|
|
83
85
|
:param logger: optional logger
|
|
84
86
|
"""
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if not item_data:
|
|
89
|
-
# build control data
|
|
87
|
+
# Do the access data already exist ?
|
|
88
|
+
if not self.assert_access_data(reference_url=reference_url):
|
|
89
|
+
# no, build control data
|
|
90
90
|
control_data: dict[str, Any] = {
|
|
91
|
-
"
|
|
91
|
+
"reference-url": reference_url,
|
|
92
92
|
"algorithm": algorithm,
|
|
93
93
|
"access-max-age": access_max_age,
|
|
94
94
|
"request-timeout": request_timeout,
|
|
95
|
+
"remote-provider": remote_provider,
|
|
95
96
|
"refresh-exp": datetime.now(tz=timezone.utc) + timedelta(seconds=refresh_max_age)
|
|
96
97
|
}
|
|
97
98
|
if algorithm in ["HS256", "HS512"]:
|
|
@@ -121,35 +122,35 @@ class JwtData:
|
|
|
121
122
|
with self.access_lock:
|
|
122
123
|
self.access_data.append(item_data)
|
|
123
124
|
if logger:
|
|
124
|
-
logger.debug(f"JWT data added for '{
|
|
125
|
+
logger.debug(f"JWT data added for '{reference_url}': {item_data}")
|
|
125
126
|
elif logger:
|
|
126
|
-
logger.warning(f"JWT data already exists for '{
|
|
127
|
+
logger.warning(f"JWT data already exists for '{reference_url}'")
|
|
127
128
|
|
|
128
129
|
def remove_access_data(self,
|
|
129
|
-
|
|
130
|
+
reference_url: str,
|
|
130
131
|
logger: Logger) -> None:
|
|
131
132
|
"""
|
|
132
|
-
Remove from storage the access data
|
|
133
|
+
Remove from storage the access data for *reference_url*.
|
|
133
134
|
|
|
134
|
-
:param
|
|
135
|
+
:param reference_url: the reference URL
|
|
135
136
|
:param logger: optional logger
|
|
136
137
|
"""
|
|
137
|
-
# obtain the item in storage
|
|
138
|
-
item_data: dict[str, dict[str, Any]] = self.retrieve_access_data(
|
|
138
|
+
# obtain the access data item in storage
|
|
139
|
+
item_data: dict[str, dict[str, Any]] = self.retrieve_access_data(reference_url=reference_url,
|
|
139
140
|
logger=logger)
|
|
140
141
|
if item_data:
|
|
141
142
|
with self.access_lock:
|
|
142
143
|
self.access_data.remove(item_data)
|
|
143
144
|
if logger:
|
|
144
|
-
logger.debug(f"Removed JWT data for '{
|
|
145
|
+
logger.debug(f"Removed JWT data for '{reference_url}'")
|
|
145
146
|
elif logger:
|
|
146
|
-
logger.warning(f"No JWT data found for '{
|
|
147
|
+
logger.warning(f"No JWT data found for '{reference_url}'")
|
|
147
148
|
|
|
148
149
|
def get_token_data(self,
|
|
149
|
-
|
|
150
|
+
reference_url: str,
|
|
150
151
|
logger: Logger = None) -> dict[str, Any]:
|
|
151
152
|
"""
|
|
152
|
-
Obtain and return the JWT token
|
|
153
|
+
Obtain and return the JWT token for *reference_url*, along with its duration.
|
|
153
154
|
|
|
154
155
|
Structure of the return data:
|
|
155
156
|
{
|
|
@@ -157,7 +158,7 @@ class JwtData:
|
|
|
157
158
|
"expires_in": <seconds-to-expiration>
|
|
158
159
|
}
|
|
159
160
|
|
|
160
|
-
:param
|
|
161
|
+
:param reference_url: the reference URL for obtaining JWT tokens
|
|
161
162
|
:param logger: optional logger
|
|
162
163
|
:return: the JWT token data, or 'None' if error
|
|
163
164
|
:raises InvalidTokenError: token is invalid
|
|
@@ -170,14 +171,14 @@ class JwtData:
|
|
|
170
171
|
:raises InvalidIssuerError: 'iss' claim does not match the expected issuer
|
|
171
172
|
:raises InvalidIssuedAtError: 'iat' claim is non-numeric
|
|
172
173
|
:raises MissingRequiredClaimError: a required claim is not contained in the claimset
|
|
173
|
-
:raises RuntimeError: access data not found for the given *
|
|
174
|
+
:raises RuntimeError: access data not found for the given *reference_url*, or
|
|
174
175
|
the remote JWT provider failed to return a token
|
|
175
176
|
"""
|
|
176
177
|
# declare the return variable
|
|
177
178
|
result: dict[str, Any]
|
|
178
179
|
|
|
179
180
|
# obtain the item in storage
|
|
180
|
-
item_data: dict[str, Any] = self.retrieve_access_data(
|
|
181
|
+
item_data: dict[str, Any] = self.retrieve_access_data(reference_url=reference_url,
|
|
181
182
|
logger=logger)
|
|
182
183
|
# was the JWT data obtained ?
|
|
183
184
|
if item_data:
|
|
@@ -190,29 +191,19 @@ class JwtData:
|
|
|
190
191
|
# is the current token still valid ?
|
|
191
192
|
if just_now > standard_claims.get("exp"):
|
|
192
193
|
# no, obtain a new token
|
|
193
|
-
|
|
194
|
+
reference_url: str = control_data.get("reference-url")
|
|
194
195
|
claims: dict[str, Any] = standard_claims.copy()
|
|
195
196
|
claims.update(custom_claims)
|
|
196
197
|
|
|
197
|
-
# where is the locus of the JWT service ?
|
|
198
|
-
if control_data.get("
|
|
199
|
-
# JWT service is
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
token: str = jwt.encode(payload=claims,
|
|
203
|
-
key=control_data.get("secret-key") or control_data.get("private-key"),
|
|
204
|
-
algorithm=control_data.get("algorithm"))
|
|
205
|
-
with self.access_lock:
|
|
206
|
-
control_data["access-token"] = token
|
|
207
|
-
standard_claims["exp"] = claims.get("exp")
|
|
208
|
-
else:
|
|
209
|
-
# JWT service is remote
|
|
210
|
-
if service_url.find("?") > 0:
|
|
211
|
-
service_url = service_url[:service_url.index("?")]
|
|
198
|
+
# where is the locus of the JWT service provider ?
|
|
199
|
+
if control_data.get("remote-provider"):
|
|
200
|
+
# JWT service is being provided by a remote server
|
|
201
|
+
if reference_url.find("?") > 0:
|
|
202
|
+
reference_url = reference_url[:reference_url.index("?")]
|
|
212
203
|
claims.pop("exp", None)
|
|
213
204
|
errors: list[str] = []
|
|
214
205
|
result = jwt_request_token(errors=errors,
|
|
215
|
-
|
|
206
|
+
reference_url=reference_url,
|
|
216
207
|
claims=claims,
|
|
217
208
|
timeout=control_data.get("request-timeout"),
|
|
218
209
|
logger=logger)
|
|
@@ -223,6 +214,16 @@ class JwtData:
|
|
|
223
214
|
standard_claims["exp"] = just_now + timedelta(seconds=duration)
|
|
224
215
|
else:
|
|
225
216
|
raise RuntimeError(" - ".join(errors))
|
|
217
|
+
else:
|
|
218
|
+
# JWT service is being provided locally
|
|
219
|
+
claims["exp"] = just_now + timedelta(seconds=control_data.get("access-max-age") + 10)
|
|
220
|
+
# may raise an exception
|
|
221
|
+
token: str = jwt.encode(payload=claims,
|
|
222
|
+
key=control_data.get("secret-key") or control_data.get("private-key"),
|
|
223
|
+
algorithm=control_data.get("algorithm"))
|
|
224
|
+
with self.access_lock:
|
|
225
|
+
control_data["access-token"] = token
|
|
226
|
+
standard_claims["exp"] = claims.get("exp")
|
|
226
227
|
|
|
227
228
|
# return the token
|
|
228
229
|
diff: timedelta = standard_claims.get("exp") - just_now - timedelta(seconds=10)
|
|
@@ -231,8 +232,8 @@ class JwtData:
|
|
|
231
232
|
"expires_in": math.trunc(diff.total_seconds())
|
|
232
233
|
}
|
|
233
234
|
else:
|
|
234
|
-
# JWT data not found
|
|
235
|
-
err_msg: str = f"No JWT data found for {
|
|
235
|
+
# JWT access data not found
|
|
236
|
+
err_msg: str = f"No JWT access data found for '{reference_url}'"
|
|
236
237
|
if logger:
|
|
237
238
|
logger.error(err_msg)
|
|
238
239
|
raise RuntimeError(err_msg)
|
|
@@ -274,49 +275,85 @@ class JwtData:
|
|
|
274
275
|
|
|
275
276
|
return result
|
|
276
277
|
|
|
278
|
+
def assert_access_data(self,
|
|
279
|
+
reference_url: str) -> bool:
|
|
280
|
+
# noinspection HttpUrlsUsage
|
|
281
|
+
"""
|
|
282
|
+
Assert whether access data exists for *reference_url*.
|
|
283
|
+
|
|
284
|
+
For the purpose of locating access data, Protocol indication in *reference_url*
|
|
285
|
+
(typically, *http://* or *https://*), is disregarded. This guarantees
|
|
286
|
+
that processing herein will not be affected by in-transit protocol changes.
|
|
287
|
+
|
|
288
|
+
:param reference_url: the reference URL for obtaining JWT tokens
|
|
289
|
+
:return: *True" is access data is in storage, *False* otherwise
|
|
290
|
+
"""
|
|
291
|
+
# initialize the return variable
|
|
292
|
+
result: bool = False
|
|
293
|
+
|
|
294
|
+
# disregard protocol
|
|
295
|
+
if reference_url.find("://") > 0:
|
|
296
|
+
reference_url = reference_url[reference_url.index("://")+3:]
|
|
297
|
+
|
|
298
|
+
# assert the data
|
|
299
|
+
with self.access_lock:
|
|
300
|
+
for item_data in self.access_data:
|
|
301
|
+
item_url: str = item_data.get("control-data").get("reference-url")
|
|
302
|
+
if item_url.find("://") > 0:
|
|
303
|
+
item_url = item_url[item_url.index("://")+3:]
|
|
304
|
+
if reference_url == item_url:
|
|
305
|
+
result = True
|
|
306
|
+
break
|
|
307
|
+
|
|
308
|
+
return result
|
|
309
|
+
|
|
277
310
|
def retrieve_access_data(self,
|
|
278
|
-
|
|
311
|
+
reference_url: str,
|
|
279
312
|
logger: Logger = None) -> dict[str, dict[str, Any]]:
|
|
280
313
|
# noinspection HttpUrlsUsage
|
|
281
314
|
"""
|
|
282
|
-
|
|
315
|
+
Retrieve and return the access data in storage for *reference_url*.
|
|
283
316
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
317
|
+
For the purpose of locating access data, Protocol indication in *reference_url*
|
|
318
|
+
(typically, *http://* or *https://*), is disregarded. This guarantees
|
|
319
|
+
that processing herein will not be affected by in-transit protocol changes.
|
|
287
320
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
321
|
+
:param reference_url: the reference URL for obtaining JWT tokens
|
|
322
|
+
:param logger: optional logger
|
|
323
|
+
:return: the corresponding item in storage, or *None* if not found
|
|
324
|
+
"""
|
|
292
325
|
# initialize the return variable
|
|
293
326
|
result: dict[str, dict[str, Any]] | None = None
|
|
327
|
+
|
|
328
|
+
if logger:
|
|
329
|
+
logger.debug(f"Retrieve access data for reference URL '{reference_url}'")
|
|
294
330
|
|
|
295
331
|
# disregard protocol
|
|
296
|
-
if
|
|
297
|
-
|
|
332
|
+
if reference_url.find("://") > 0:
|
|
333
|
+
reference_url = reference_url[reference_url.index("://")+3:]
|
|
298
334
|
|
|
335
|
+
# retrieve the data
|
|
299
336
|
with self.access_lock:
|
|
300
337
|
for item_data in self.access_data:
|
|
301
|
-
item_url: str = item_data.get("control-data").get("
|
|
338
|
+
item_url: str = item_data.get("control-data").get("reference-url")
|
|
302
339
|
if item_url.find("://") > 0:
|
|
303
340
|
item_url = item_url[item_url.index("://")+3:]
|
|
304
|
-
if
|
|
341
|
+
if reference_url == item_url:
|
|
305
342
|
result = item_data
|
|
306
343
|
break
|
|
307
344
|
if logger:
|
|
308
|
-
logger.debug(f"
|
|
345
|
+
logger.debug(f"Data is '{result}'")
|
|
309
346
|
|
|
310
347
|
return result
|
|
311
348
|
|
|
312
349
|
|
|
313
350
|
def jwt_request_token(errors: list[str],
|
|
314
|
-
|
|
351
|
+
reference_url: str,
|
|
315
352
|
claims: dict[str, Any],
|
|
316
353
|
timeout: float = None,
|
|
317
354
|
logger: Logger = None) -> dict[str, Any]:
|
|
318
355
|
"""
|
|
319
|
-
Obtain and return the JWT token associated with *
|
|
356
|
+
Obtain and return the JWT token associated with *reference_url*, along with its duration.
|
|
320
357
|
|
|
321
358
|
Expected structure of the return data:
|
|
322
359
|
{
|
|
@@ -327,7 +364,7 @@ def jwt_request_token(errors: list[str],
|
|
|
327
364
|
of the provider issuing the JWT token.
|
|
328
365
|
|
|
329
366
|
:param errors: incidental errors
|
|
330
|
-
:param
|
|
367
|
+
:param reference_url: the reference URL for obtaining JWT tokens
|
|
331
368
|
:param claims: the JWT claimset, as expected by the issuing server
|
|
332
369
|
:param timeout: request timeout, in seconds (defaults to *None*)
|
|
333
370
|
:param logger: optional logger
|
|
@@ -337,9 +374,9 @@ def jwt_request_token(errors: list[str],
|
|
|
337
374
|
|
|
338
375
|
# request the JWT token
|
|
339
376
|
if logger:
|
|
340
|
-
logger.debug(f"POST request JWT token to '{
|
|
377
|
+
logger.debug(f"POST request JWT token to '{reference_url}'")
|
|
341
378
|
response: Response = requests.post(
|
|
342
|
-
url=
|
|
379
|
+
url=reference_url,
|
|
343
380
|
json=claims,
|
|
344
381
|
timeout=timeout
|
|
345
382
|
)
|
|
@@ -352,7 +389,7 @@ def jwt_request_token(errors: list[str],
|
|
|
352
389
|
logger.debug(f"JWT token obtained: {result}")
|
|
353
390
|
else:
|
|
354
391
|
# no, report the problem
|
|
355
|
-
err_msg: str = f"POST request of '{
|
|
392
|
+
err_msg: str = f"POST request of '{reference_url}' failed: {response.reason}"
|
|
356
393
|
if response.text:
|
|
357
394
|
err_msg += f" - {response.text}"
|
|
358
395
|
if logger:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
from flask import Request, Response, request, jsonify
|
|
3
3
|
from logging import Logger
|
|
4
|
-
|
|
4
|
+
from OpenSSL import crypto
|
|
5
5
|
from pypomes_core import APP_PREFIX, env_get_str, env_get_bytes, env_get_int
|
|
6
6
|
from secrets import token_bytes
|
|
7
7
|
from typing import Any, Final, Literal
|
|
@@ -21,10 +21,10 @@ JWT_ENDPOINT_URL: Final[str] = env_get_str(key=f"{APP_PREFIX}_JWT_ENDPOINT_URL")
|
|
|
21
21
|
|
|
22
22
|
__priv_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_RSA_PRIVATE_KEY")
|
|
23
23
|
__pub_key: bytes = env_get_bytes(key=f"{APP_PREFIX}_JWT_RSA_PUBLIC_KEY")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
if not __priv_key or not __pub_key:
|
|
25
|
+
pk = crypto.PKey()
|
|
26
|
+
__priv_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pk)
|
|
27
|
+
__pub_key = crypto.dump_publickey(crypto.FILETYPE_PEM, pk)
|
|
28
28
|
JWT_RSA_PRIVATE_KEY: Final[bytes] = __priv_key
|
|
29
29
|
JWT_RSA_PUBLIC_KEY: Final[bytes] = __pub_key
|
|
30
30
|
|
|
@@ -49,7 +49,7 @@ def jwt_needed(func: callable) -> callable:
|
|
|
49
49
|
return wrapper
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def jwt_set_service_access(
|
|
52
|
+
def jwt_set_service_access(reference_url: str,
|
|
53
53
|
claims: dict[str, Any],
|
|
54
54
|
algorithm: Literal["HS256", "HS512", "RSA256", "RSA512"] = JWT_DEFAULT_ALGORITHM,
|
|
55
55
|
access_max_age: int = JWT_ACCESS_MAX_AGE,
|
|
@@ -58,11 +58,12 @@ def jwt_set_service_access(service_url: str,
|
|
|
58
58
|
private_key: bytes = JWT_RSA_PRIVATE_KEY,
|
|
59
59
|
public_key: bytes = JWT_RSA_PUBLIC_KEY,
|
|
60
60
|
request_timeout: int = None,
|
|
61
|
+
remote_provider: bool = True,
|
|
61
62
|
logger: Logger = None) -> None:
|
|
62
63
|
"""
|
|
63
|
-
Set the data needed to obtain JWT tokens from *
|
|
64
|
+
Set the data needed to obtain JWT tokens from *reference_url*.
|
|
64
65
|
|
|
65
|
-
:param
|
|
66
|
+
:param reference_url: the reference URL
|
|
66
67
|
:param claims: the JWT claimset, as key-value pairs
|
|
67
68
|
:param algorithm: the authentication type
|
|
68
69
|
:param access_max_age: token duration, in seconds
|
|
@@ -71,18 +72,22 @@ def jwt_set_service_access(service_url: str,
|
|
|
71
72
|
:param private_key: private key for RSA authentication
|
|
72
73
|
:param public_key: public key for RSA authentication
|
|
73
74
|
:param request_timeout: timeout for the requests to the service URL
|
|
75
|
+
:param remote_provider: whether the JWT provider is a remote server
|
|
74
76
|
:param logger: optional logger
|
|
75
77
|
"""
|
|
78
|
+
if logger:
|
|
79
|
+
logger.debug(msg=f"Register access data for '{reference_url}'")
|
|
76
80
|
# extract the extra claims
|
|
77
|
-
pos: int =
|
|
81
|
+
pos: int = reference_url.find("?")
|
|
78
82
|
if pos > 0:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
if remote_provider:
|
|
84
|
+
params: list[str] = reference_url[pos+1:].split(sep="&")
|
|
85
|
+
for param in params:
|
|
86
|
+
claims[param.split("=")[0]] = param.split("=")[1]
|
|
87
|
+
reference_url = reference_url[:pos]
|
|
83
88
|
|
|
84
89
|
# register the JWT service
|
|
85
|
-
__jwt_data.add_access_data(
|
|
90
|
+
__jwt_data.add_access_data(reference_url=reference_url,
|
|
86
91
|
claims=claims,
|
|
87
92
|
algorithm=algorithm,
|
|
88
93
|
access_max_age=access_max_age,
|
|
@@ -91,39 +96,48 @@ def jwt_set_service_access(service_url: str,
|
|
|
91
96
|
private_key=private_key,
|
|
92
97
|
public_key=public_key,
|
|
93
98
|
request_timeout=request_timeout,
|
|
99
|
+
remote_provider=remote_provider,
|
|
94
100
|
logger=logger)
|
|
95
101
|
|
|
96
102
|
|
|
97
|
-
def jwt_remove_service_access(
|
|
103
|
+
def jwt_remove_service_access(reference_url: str,
|
|
98
104
|
logger: Logger = None) -> None:
|
|
99
105
|
"""
|
|
100
|
-
Remove from storage the JWT access data for *
|
|
106
|
+
Remove from storage the JWT access data for *reference_url*.
|
|
101
107
|
|
|
102
|
-
:param
|
|
108
|
+
:param reference_url: the reference URL
|
|
103
109
|
:param logger: optional logger
|
|
104
110
|
"""
|
|
105
|
-
|
|
111
|
+
if logger:
|
|
112
|
+
logger.debug(msg=f"Remove access data for '{reference_url}'")
|
|
113
|
+
|
|
114
|
+
__jwt_data.remove_access_data(reference_url=reference_url,
|
|
106
115
|
logger=logger)
|
|
107
116
|
|
|
108
117
|
|
|
109
118
|
def jwt_get_token(errors: list[str],
|
|
110
|
-
|
|
119
|
+
reference_url: str,
|
|
111
120
|
logger: Logger = None) -> str:
|
|
112
121
|
"""
|
|
113
|
-
Obtain and return a JWT token from *
|
|
122
|
+
Obtain and return a JWT token from *reference_url*.
|
|
114
123
|
|
|
115
124
|
:param errors: incidental error messages
|
|
116
|
-
:param
|
|
125
|
+
:param reference_url: the reference URL
|
|
117
126
|
:param logger: optional logger
|
|
118
127
|
:return: the JWT token, or 'None' if an error ocurred
|
|
119
128
|
"""
|
|
120
129
|
# inicialize the return variable
|
|
121
130
|
result: str | None = None
|
|
122
131
|
|
|
132
|
+
if logger:
|
|
133
|
+
logger.debug(msg=f"Obtain a JWT token for '{reference_url}'")
|
|
134
|
+
|
|
123
135
|
try:
|
|
124
|
-
token_data: dict[str, Any] = __jwt_data.get_token_data(
|
|
136
|
+
token_data: dict[str, Any] = __jwt_data.get_token_data(reference_url=reference_url,
|
|
125
137
|
logger=logger)
|
|
126
138
|
result = token_data.get("access_token")
|
|
139
|
+
if logger:
|
|
140
|
+
logger.debug(f"Token is '{result}'")
|
|
127
141
|
except Exception as e:
|
|
128
142
|
if logger:
|
|
129
143
|
logger.error(msg=str(e))
|
|
@@ -133,10 +147,10 @@ def jwt_get_token(errors: list[str],
|
|
|
133
147
|
|
|
134
148
|
|
|
135
149
|
def jwt_get_token_data(errors: list[str],
|
|
136
|
-
|
|
150
|
+
reference_url: str,
|
|
137
151
|
logger: Logger = None) -> dict[str, Any]:
|
|
138
152
|
"""
|
|
139
|
-
Obtain and return the JWT token associated with *
|
|
153
|
+
Obtain and return the JWT token associated with *reference_url*, along with its duration.
|
|
140
154
|
|
|
141
155
|
Structure of the return data:
|
|
142
156
|
{
|
|
@@ -145,16 +159,20 @@ def jwt_get_token_data(errors: list[str],
|
|
|
145
159
|
}
|
|
146
160
|
|
|
147
161
|
:param errors: incidental error messages
|
|
148
|
-
:param
|
|
162
|
+
:param reference_url: the reference URL for obtaining JWT tokens
|
|
149
163
|
:param logger: optional logger
|
|
150
164
|
:return: the JWT token data, or 'None' if error
|
|
151
165
|
"""
|
|
152
166
|
# inicialize the return variable
|
|
153
167
|
result: dict[str, Any] | None = None
|
|
154
168
|
|
|
169
|
+
if logger:
|
|
170
|
+
logger.debug(msg=f"Retrieve JWT token data for '{reference_url}'")
|
|
155
171
|
try:
|
|
156
|
-
result = __jwt_data.get_token_data(
|
|
172
|
+
result = __jwt_data.get_token_data(reference_url=reference_url,
|
|
157
173
|
logger=logger)
|
|
174
|
+
if logger:
|
|
175
|
+
logger.debug(msg=f"Data is '{result}'")
|
|
158
176
|
except Exception as e:
|
|
159
177
|
if logger:
|
|
160
178
|
logger.error(msg=str(e))
|
|
@@ -177,6 +195,9 @@ def jwt_get_claims(errors: list[str],
|
|
|
177
195
|
# initialize the return variable
|
|
178
196
|
result: dict[str, Any] | None = None
|
|
179
197
|
|
|
198
|
+
if logger:
|
|
199
|
+
logger.debug(msg=f"Retrieve claims for token '{token}'")
|
|
200
|
+
|
|
180
201
|
try:
|
|
181
202
|
result = __jwt_data.get_token_claims(token=token)
|
|
182
203
|
except Exception as e:
|
|
@@ -198,6 +219,9 @@ def jwt_verify_request(request: Request,
|
|
|
198
219
|
"""
|
|
199
220
|
# initialize the return variable
|
|
200
221
|
result: Response | None = None
|
|
222
|
+
|
|
223
|
+
if logger:
|
|
224
|
+
logger.debug(msg="Validate a JWT token")
|
|
201
225
|
|
|
202
226
|
# retrieve the authorization from the request header
|
|
203
227
|
auth_header: str = request.headers.get("Authorization")
|
|
@@ -206,6 +230,8 @@ def jwt_verify_request(request: Request,
|
|
|
206
230
|
if auth_header and auth_header.startswith("Bearer "):
|
|
207
231
|
# yes, extract and validate the JWT token
|
|
208
232
|
token: str = auth_header.split(" ")[1]
|
|
233
|
+
if logger:
|
|
234
|
+
logger.debug(msg=f"Token is '{token}'")
|
|
209
235
|
try:
|
|
210
236
|
jwt_validate_token(token=token,
|
|
211
237
|
key=JWT_HS_SECRET_KEY or JWT_RSA_PUBLIC_KEY,
|
|
@@ -218,13 +244,15 @@ def jwt_verify_request(request: Request,
|
|
|
218
244
|
status=401)
|
|
219
245
|
else:
|
|
220
246
|
# no, report the error
|
|
247
|
+
if logger:
|
|
248
|
+
logger.error(msg="Request header has no 'Bearer' data")
|
|
221
249
|
result = Response(response="Authorization failed",
|
|
222
250
|
status=401)
|
|
223
251
|
|
|
224
252
|
return result
|
|
225
253
|
|
|
226
254
|
|
|
227
|
-
def jwt_service(
|
|
255
|
+
def jwt_service(reference_url: str = None,
|
|
228
256
|
service_params: dict[str, Any] = None,
|
|
229
257
|
logger: Logger = None) -> Response:
|
|
230
258
|
"""
|
|
@@ -233,7 +261,7 @@ def jwt_service(service_url: str = None,
|
|
|
233
261
|
In order to be serviced, the invoker must send, as parameter *service_params* or in the body of the request,
|
|
234
262
|
a JSON containing:
|
|
235
263
|
{
|
|
236
|
-
"
|
|
264
|
+
"reference-url": "<url>", - the JWT reference URL (if not as parameter)
|
|
237
265
|
"<custom-claim-key-1>": "<custom-claim-value-1>", - the registered custom claims
|
|
238
266
|
...
|
|
239
267
|
"<custom-claim-key-n>": "<custom-claim-value-n>"
|
|
@@ -245,7 +273,7 @@ def jwt_service(service_url: str = None,
|
|
|
245
273
|
"expires_in": <seconds-to-expiration>
|
|
246
274
|
}
|
|
247
275
|
|
|
248
|
-
:param
|
|
276
|
+
:param reference_url: the JWT reference URL, alternatively passed in JSON
|
|
249
277
|
:param service_params: the optional JSON containing the request parameters (defaults to JSON in body)
|
|
250
278
|
:param logger: optional logger
|
|
251
279
|
:return: the requested JWT token, along with its duration.
|
|
@@ -253,6 +281,12 @@ def jwt_service(service_url: str = None,
|
|
|
253
281
|
# declare the return variable
|
|
254
282
|
result: Response
|
|
255
283
|
|
|
284
|
+
if logger:
|
|
285
|
+
msg: str = "Service a JWT request"
|
|
286
|
+
if request:
|
|
287
|
+
msg += f" from '{request.base_url}'"
|
|
288
|
+
logger.debug(msg=msg)
|
|
289
|
+
|
|
256
290
|
# obtain the parameters
|
|
257
291
|
# noinspection PyUnusedLocal
|
|
258
292
|
params: dict[str, Any] = service_params or {}
|
|
@@ -262,10 +296,12 @@ def jwt_service(service_url: str = None,
|
|
|
262
296
|
|
|
263
297
|
# validate the parameters
|
|
264
298
|
valid: bool = False
|
|
265
|
-
if not
|
|
266
|
-
|
|
267
|
-
if
|
|
268
|
-
|
|
299
|
+
if not reference_url:
|
|
300
|
+
reference_url = params.get("reference-url")
|
|
301
|
+
if reference_url:
|
|
302
|
+
if logger:
|
|
303
|
+
logger.debug(msg=f"Reference URL is '{reference_url}'")
|
|
304
|
+
item_data: dict[str, dict[str, Any]] = __jwt_data.retrieve_access_data(reference_url=reference_url,
|
|
269
305
|
logger=logger)
|
|
270
306
|
if item_data:
|
|
271
307
|
valid = True
|
|
@@ -278,7 +314,7 @@ def jwt_service(service_url: str = None,
|
|
|
278
314
|
# obtain the token data
|
|
279
315
|
if valid:
|
|
280
316
|
try:
|
|
281
|
-
token_data: dict[str, Any] = __jwt_data.get_token_data(
|
|
317
|
+
token_data: dict[str, Any] = __jwt_data.get_token_data(reference_url=reference_url,
|
|
282
318
|
logger=logger)
|
|
283
319
|
result = jsonify(token_data)
|
|
284
320
|
except Exception as e:
|
|
@@ -288,6 +324,8 @@ def jwt_service(service_url: str = None,
|
|
|
288
324
|
result = Response(response=str(e),
|
|
289
325
|
status=401)
|
|
290
326
|
else:
|
|
327
|
+
if logger:
|
|
328
|
+
logger.debug(msg=f"Invalid parameters {service_params}")
|
|
291
329
|
result = Response(response="Invalid parameters",
|
|
292
330
|
status=401)
|
|
293
331
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|