pypomes-jwt 0.6.4__tar.gz → 0.6.6__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.6.4 → pypomes_jwt-0.6.6}/PKG-INFO +1 -1
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/pyproject.toml +1 -1
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/src/pypomes_jwt/__init__.py +4 -4
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/src/pypomes_jwt/jwt_data.py +8 -3
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/src/pypomes_jwt/jwt_pomes.py +56 -65
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/.gitignore +0 -0
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/LICENSE +0 -0
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/README.md +0 -0
- {pypomes_jwt-0.6.4 → pypomes_jwt-0.6.6}/src/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_jwt
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.6
|
|
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
|
|
@@ -5,8 +5,8 @@ from .jwt_pomes import (
|
|
|
5
5
|
JWT_ENDPOINT_URL,
|
|
6
6
|
JWT_ACCESS_MAX_AGE, JWT_REFRESH_MAX_AGE,
|
|
7
7
|
JWT_HS_SECRET_KEY, JWT_RSA_PRIVATE_KEY, JWT_RSA_PUBLIC_KEY,
|
|
8
|
-
jwt_needed, jwt_verify_request,
|
|
9
|
-
|
|
8
|
+
jwt_needed, jwt_verify_request, jwt_claims, jwt_token,
|
|
9
|
+
jwt_get_token_data, jwt_get_token_claims,
|
|
10
10
|
jwt_assert_access, jwt_set_access, jwt_remove_access
|
|
11
11
|
)
|
|
12
12
|
|
|
@@ -17,8 +17,8 @@ __all__ = [
|
|
|
17
17
|
"JWT_ENDPOINT_URL",
|
|
18
18
|
"JWT_ACCESS_MAX_AGE", "JWT_REFRESH_MAX_AGE",
|
|
19
19
|
"JWT_HS_SECRET_KEY", "JWT_RSA_PRIVATE_KEY", "JWT_RSA_PUBLIC_KEY",
|
|
20
|
-
"jwt_needed", "jwt_verify_request", "
|
|
21
|
-
"
|
|
20
|
+
"jwt_needed", "jwt_verify_request", "jwt_claims", "jwt_token",
|
|
21
|
+
"jwt_get_token_data", "jwt_get_token_claims",
|
|
22
22
|
"jwt_assert_access", "jwt_set_access", "jwt_remove_access"
|
|
23
23
|
]
|
|
24
24
|
|
|
@@ -39,14 +39,14 @@ class JwtData:
|
|
|
39
39
|
# "aud": <string> # audience
|
|
40
40
|
# "nbt": <timestamp> # not before time
|
|
41
41
|
},
|
|
42
|
-
"public-claims": {
|
|
42
|
+
"public-claims": { # public claims (may be empty)
|
|
43
43
|
"birthdate": <string>, # subject's birth date
|
|
44
44
|
"email": <string>, # subject's email
|
|
45
45
|
"gender": <string>, # subject's gender
|
|
46
46
|
"name": <string>, # subject's name
|
|
47
47
|
"roles": <List[str]> # subject roles
|
|
48
48
|
},
|
|
49
|
-
"custom-claims": { # custom claims
|
|
49
|
+
"custom-claims": { # custom claims (may be empty)
|
|
50
50
|
"<custom-claim-key-1>": "<custom-claim-value-1>",
|
|
51
51
|
...
|
|
52
52
|
"<custom-claim-key-n>": "<custom-claim-value-n>"
|
|
@@ -166,6 +166,7 @@ class JwtData:
|
|
|
166
166
|
|
|
167
167
|
def get_token_data(self,
|
|
168
168
|
account_id: str,
|
|
169
|
+
superceding_claims: dict[str, Any] = None,
|
|
169
170
|
logger: Logger = None) -> dict[str, Any]:
|
|
170
171
|
"""
|
|
171
172
|
Obtain and return the JWT token for *account_id*, along with its duration.
|
|
@@ -178,6 +179,7 @@ class JwtData:
|
|
|
178
179
|
}
|
|
179
180
|
|
|
180
181
|
:param account_id: the account identification
|
|
182
|
+
:param superceding_claims: if provided, may supercede registered custom claims
|
|
181
183
|
:param logger: optional logger
|
|
182
184
|
:return: the JWT token data, or *None* if error
|
|
183
185
|
:raises InvalidTokenError: token is invalid
|
|
@@ -205,9 +207,12 @@ class JwtData:
|
|
|
205
207
|
control_data: dict[str, Any] = item_data.get("control-data")
|
|
206
208
|
reserved_claims: dict[str, Any] = item_data.get("reserved-claims")
|
|
207
209
|
custom_claims: dict[str, Any] = item_data.get("custom-claims")
|
|
208
|
-
|
|
210
|
+
if superceding_claims:
|
|
211
|
+
custom_claims = custom_claims.copy()
|
|
212
|
+
custom_claims.update(m=superceding_claims)
|
|
209
213
|
|
|
210
214
|
# obtain a new token, if the current token has expired
|
|
215
|
+
just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
|
|
211
216
|
if just_now > reserved_claims.get("exp"):
|
|
212
217
|
# where is the JWT service provider ?
|
|
213
218
|
if control_data.get("remote-provider"):
|
|
@@ -137,42 +137,12 @@ def jwt_remove_access(account_id: str,
|
|
|
137
137
|
logger=logger)
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
def jwt_get_token(errors: list[str],
|
|
141
|
-
account_id: str,
|
|
142
|
-
logger: Logger = None) -> str:
|
|
143
|
-
"""
|
|
144
|
-
Obtain and return a JWT token for *account_id*.
|
|
145
|
-
|
|
146
|
-
:param errors: incidental error messages
|
|
147
|
-
:param account_id: the account identification
|
|
148
|
-
:param logger: optional logger
|
|
149
|
-
:return: the JWT token, or *None* if an error ocurred
|
|
150
|
-
"""
|
|
151
|
-
# inicialize the return variable
|
|
152
|
-
result: str | None = None
|
|
153
|
-
|
|
154
|
-
if logger:
|
|
155
|
-
logger.debug(msg=f"Obtain a JWT token for '{account_id}'")
|
|
156
|
-
|
|
157
|
-
try:
|
|
158
|
-
token_data: dict[str, Any] = __jwt_data.get_token_data(account_id=account_id,
|
|
159
|
-
logger=logger)
|
|
160
|
-
result = token_data.get("access_token")
|
|
161
|
-
if logger:
|
|
162
|
-
logger.debug(f"Token is '{result}'")
|
|
163
|
-
except Exception as e:
|
|
164
|
-
if logger:
|
|
165
|
-
logger.error(msg=str(e))
|
|
166
|
-
errors.append(str(e))
|
|
167
|
-
|
|
168
|
-
return result
|
|
169
|
-
|
|
170
|
-
|
|
171
140
|
def jwt_get_token_data(errors: list[str],
|
|
172
141
|
account_id: str,
|
|
142
|
+
superceding_claims: dict[str, Any] = None,
|
|
173
143
|
logger: Logger = None) -> dict[str, Any]:
|
|
174
144
|
"""
|
|
175
|
-
Obtain and return the JWT token associated with *account_id
|
|
145
|
+
Obtain and return the JWT token data associated with *account_id*.
|
|
176
146
|
|
|
177
147
|
Structure of the return data:
|
|
178
148
|
{
|
|
@@ -183,6 +153,7 @@ def jwt_get_token_data(errors: list[str],
|
|
|
183
153
|
|
|
184
154
|
:param errors: incidental error messages
|
|
185
155
|
:param account_id: the account identification
|
|
156
|
+
:param superceding_claims: if provided, may supercede registered custom claims
|
|
186
157
|
:param logger: optional logger
|
|
187
158
|
:return: the JWT token data, or *None* if error
|
|
188
159
|
"""
|
|
@@ -193,6 +164,7 @@ def jwt_get_token_data(errors: list[str],
|
|
|
193
164
|
logger.debug(msg=f"Retrieve JWT token data for '{account_id}'")
|
|
194
165
|
try:
|
|
195
166
|
result = __jwt_data.get_token_data(account_id=account_id,
|
|
167
|
+
superceding_claims=superceding_claims,
|
|
196
168
|
logger=logger)
|
|
197
169
|
if logger:
|
|
198
170
|
logger.debug(msg=f"Data is '{result}'")
|
|
@@ -290,20 +262,61 @@ def jwt_verify_request(request: Request,
|
|
|
290
262
|
return result
|
|
291
263
|
|
|
292
264
|
|
|
293
|
-
def
|
|
294
|
-
service_params: dict[str, Any] = None,
|
|
295
|
-
logger: Logger = None) -> Response:
|
|
265
|
+
def jwt_claims(token: str = None) -> Response:
|
|
296
266
|
"""
|
|
297
|
-
|
|
267
|
+
REST service entry point for retrieving the claims of a JWT token.
|
|
298
268
|
|
|
299
|
-
|
|
269
|
+
Structure of the return data:
|
|
270
|
+
{
|
|
271
|
+
"<claim-1>": <value-of-claim-1>,
|
|
272
|
+
...
|
|
273
|
+
"<claim-n>": <value-of-claim-n>
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
:param token: the JWT token
|
|
277
|
+
:return: a *Response* containing the requested JWT token claims, or reporting an error
|
|
278
|
+
"""
|
|
279
|
+
# declare the return variable
|
|
280
|
+
result: Response
|
|
281
|
+
|
|
282
|
+
# retrieve the token
|
|
283
|
+
# noinspection PyUnusedLocal
|
|
284
|
+
if not token:
|
|
285
|
+
token = request.values.get("token")
|
|
286
|
+
if not token:
|
|
287
|
+
with contextlib.suppress(Exception):
|
|
288
|
+
token = request.get_json().get("token")
|
|
289
|
+
|
|
290
|
+
# has the token been obtained ?
|
|
291
|
+
if token:
|
|
292
|
+
# yes, obtain the token data
|
|
293
|
+
try:
|
|
294
|
+
token_claims: dict[str, Any] = __jwt_data.get_token_claims(token=token)
|
|
295
|
+
result = jsonify(token_claims)
|
|
296
|
+
except Exception as e:
|
|
297
|
+
# claims extraction failed
|
|
298
|
+
result = Response(response=str(e),
|
|
299
|
+
status=400)
|
|
300
|
+
else:
|
|
301
|
+
# no, report the problem
|
|
302
|
+
result = Response(response="Invalid parameters",
|
|
303
|
+
status=400)
|
|
304
|
+
|
|
305
|
+
return result
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def jwt_token(service_params: dict[str, Any] = None) -> Response:
|
|
309
|
+
"""
|
|
310
|
+
REST service entry point for obtaining JWT tokens.
|
|
311
|
+
|
|
312
|
+
The requester must send, as parameter *service_params* or in the body of the request:
|
|
300
313
|
{
|
|
301
314
|
"account-id": "<string>" - required account identification
|
|
302
|
-
"<custom-claim-key-1>": "<custom-claim-value-1>", - optional custom claims
|
|
315
|
+
"<custom-claim-key-1>": "<custom-claim-value-1>", - optional superceding custom claims
|
|
303
316
|
...
|
|
304
317
|
"<custom-claim-key-n>": "<custom-claim-value-n>"
|
|
305
318
|
}
|
|
306
|
-
If provided, the
|
|
319
|
+
If provided, the superceding custom claims will be sent to the remote provider, if applicable
|
|
307
320
|
(custom claims currently registered for the account may be overridden).
|
|
308
321
|
|
|
309
322
|
|
|
@@ -314,55 +327,33 @@ def jwt_service(account_id: str = None,
|
|
|
314
327
|
"expires_in": <seconds-to-expiration>
|
|
315
328
|
}
|
|
316
329
|
|
|
317
|
-
:param account_id: the account identification, alternatively passed in JSON
|
|
318
330
|
:param service_params: the optional JSON containing the request parameters (defaults to JSON in body)
|
|
319
|
-
:
|
|
320
|
-
:return: a *Response* containing the requested JWT token and its duration, or reporting an error
|
|
331
|
+
:return: a *Response* containing the requested JWT token data, or reporting an error
|
|
321
332
|
"""
|
|
322
333
|
# declare the return variable
|
|
323
334
|
result: Response
|
|
324
335
|
|
|
325
|
-
if logger:
|
|
326
|
-
msg: str = "Service a JWT request"
|
|
327
|
-
if request:
|
|
328
|
-
msg += f" from '{request.base_url}'"
|
|
329
|
-
logger.debug(msg=msg)
|
|
330
|
-
|
|
331
336
|
# retrieve the parameters
|
|
332
337
|
# noinspection PyUnusedLocal
|
|
333
338
|
params: dict[str, Any] = service_params or {}
|
|
334
339
|
if not params:
|
|
335
340
|
with contextlib.suppress(Exception):
|
|
336
341
|
params = request.get_json()
|
|
337
|
-
|
|
338
|
-
account_id = params.get("account-id")
|
|
342
|
+
account_id: str | None = params.pop("account-id", None)
|
|
339
343
|
|
|
340
344
|
# has the account been identified ?
|
|
341
345
|
if account_id:
|
|
342
|
-
# yes,
|
|
343
|
-
if logger:
|
|
344
|
-
logger.debug(msg=f"Account identification is '{account_id}'")
|
|
345
|
-
item_data: dict[str, dict[str, Any]] = __jwt_data.get_access_data(account_id=account_id,
|
|
346
|
-
logger=logger) or {}
|
|
347
|
-
custom_claims: dict[str, Any] = item_data.get("custom-claims").copy()
|
|
348
|
-
for key, value in params.items():
|
|
349
|
-
custom_claims[key] = value
|
|
350
|
-
|
|
351
|
-
# obtain the token data
|
|
346
|
+
# yes, obtain the token data
|
|
352
347
|
try:
|
|
353
348
|
token_data: dict[str, Any] = __jwt_data.get_token_data(account_id=account_id,
|
|
354
|
-
|
|
349
|
+
superceding_claims=params)
|
|
355
350
|
result = jsonify(token_data)
|
|
356
351
|
except Exception as e:
|
|
357
352
|
# token validation failed
|
|
358
|
-
if logger:
|
|
359
|
-
logger.error(msg=str(e))
|
|
360
353
|
result = Response(response=str(e),
|
|
361
354
|
status=401)
|
|
362
355
|
else:
|
|
363
356
|
# no, report the problem
|
|
364
|
-
if logger:
|
|
365
|
-
logger.debug(msg=f"Invalid parameters {service_params}")
|
|
366
357
|
result = Response(response="Invalid parameters",
|
|
367
358
|
status=401)
|
|
368
359
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|