pypomes-jwt 1.2.8__tar.gz → 1.2.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-1.2.8 → pypomes_jwt-1.2.9}/PKG-INFO +2 -2
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/pyproject.toml +2 -2
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/src/pypomes_jwt/jwt_config.py +6 -1
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/src/pypomes_jwt/jwt_pomes.py +14 -14
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/src/pypomes_jwt/jwt_providers.py +3 -3
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/src/pypomes_jwt/jwt_registry.py +94 -94
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/.gitignore +0 -0
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/LICENSE +0 -0
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.9}/README.md +0 -0
- {pypomes_jwt-1.2.8 → pypomes_jwt-1.2.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: 1.2.
|
|
3
|
+
Version: 1.2.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
|
|
@@ -14,4 +14,4 @@ Requires-Dist: cryptography>=45.0.6
|
|
|
14
14
|
Requires-Dist: flask>=3.1.2
|
|
15
15
|
Requires-Dist: pyjwt>=2.10.1
|
|
16
16
|
Requires-Dist: pypomes-core>=2.7.0
|
|
17
|
-
Requires-Dist: pypomes-db>=2.5.
|
|
17
|
+
Requires-Dist: pypomes-db>=2.5.9
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pypomes_jwt"
|
|
9
|
-
version = "1.2.
|
|
9
|
+
version = "1.2.9"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="GT Nunes", email="wisecoder01@gmail.com" }
|
|
12
12
|
]
|
|
@@ -23,7 +23,7 @@ dependencies = [
|
|
|
23
23
|
"Flask>=3.1.2",
|
|
24
24
|
"PyJWT>=2.10.1",
|
|
25
25
|
"pypomes_core>=2.7.0",
|
|
26
|
-
"pypomes_db>=2.5.
|
|
26
|
+
"pypomes_db>=2.5.9"
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.urls]
|
|
@@ -52,14 +52,19 @@ class JwtConfig(Enum):
|
|
|
52
52
|
def_value=300)
|
|
53
53
|
ACCOUNT_LIMIT: int = env_get_int(key=f"{APP_PREFIX}_JWT_ACCOUNT_LIMIT",
|
|
54
54
|
def_value=5)
|
|
55
|
+
DEFAULT_ALGORITHM: _default_algorithm
|
|
55
56
|
ENCODING_KEY: bytes = _encoding_key
|
|
56
57
|
DECODING_KEY: bytes = _decoding_key
|
|
57
|
-
DEFAULT_ALGORITHM: str = _default_algorithm
|
|
58
58
|
# recommended: at least 2 hours (set to 24 hours)
|
|
59
59
|
REFRESH_MAX_AGE: int = env_get_int(key=f"{APP_PREFIX}_JWT_REFRESH_MAX_AGE",
|
|
60
60
|
def_value=86400)
|
|
61
61
|
|
|
62
62
|
|
|
63
|
+
del _decoding_key
|
|
64
|
+
del _encoding_key
|
|
65
|
+
del _default_algorithm
|
|
66
|
+
|
|
67
|
+
|
|
63
68
|
class JwtDbConfig(StrEnum):
|
|
64
69
|
"""
|
|
65
70
|
Parameters for JWT database connection.
|
|
@@ -440,7 +440,10 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
440
440
|
|
|
441
441
|
if logger:
|
|
442
442
|
logger.debug(msg=f"Refreshing a JWT token pair for '{account_id}'")
|
|
443
|
-
|
|
443
|
+
|
|
444
|
+
# make sure to have an errors list
|
|
445
|
+
if not isinstance(errors, list):
|
|
446
|
+
errors = []
|
|
444
447
|
|
|
445
448
|
# assert the refresh token
|
|
446
449
|
if refresh_token:
|
|
@@ -448,7 +451,7 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
448
451
|
token_claims: dict[str, Any] = jwt_validate_token(token=refresh_token,
|
|
449
452
|
nature="R",
|
|
450
453
|
account_id=account_id,
|
|
451
|
-
errors=
|
|
454
|
+
errors=errors,
|
|
452
455
|
logger=logger)
|
|
453
456
|
if token_claims:
|
|
454
457
|
# yes, proceed
|
|
@@ -457,7 +460,7 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
457
460
|
# start the database transaction
|
|
458
461
|
db_conn: Any = db_connect(autocommit=False,
|
|
459
462
|
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
460
|
-
errors=
|
|
463
|
+
errors=errors,
|
|
461
464
|
logger=logger)
|
|
462
465
|
if db_conn:
|
|
463
466
|
# delete current refresh token
|
|
@@ -469,11 +472,11 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
469
472
|
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
470
473
|
connection=db_conn,
|
|
471
474
|
committable=False,
|
|
472
|
-
errors=
|
|
475
|
+
errors=errors,
|
|
473
476
|
logger=logger)
|
|
474
477
|
|
|
475
478
|
# issue the token pair
|
|
476
|
-
if not
|
|
479
|
+
if not errors:
|
|
477
480
|
try:
|
|
478
481
|
result = __jwt_registry.issue_tokens(account_id=account_id,
|
|
479
482
|
account_claims=token_claims.get("payload"),
|
|
@@ -487,27 +490,24 @@ def jwt_refresh_tokens(account_id: str,
|
|
|
487
490
|
exc_info=sys.exc_info())
|
|
488
491
|
if logger:
|
|
489
492
|
logger.error(msg=f"Error refreshing the token pair: {exc_err}")
|
|
490
|
-
|
|
493
|
+
errors.append(exc_err)
|
|
491
494
|
|
|
492
495
|
# wrap-up the transaction
|
|
493
|
-
if
|
|
496
|
+
if errors:
|
|
494
497
|
db_rollback(connection=db_conn,
|
|
495
498
|
logger=logger)
|
|
496
499
|
else:
|
|
497
500
|
db_commit(connection=db_conn,
|
|
498
|
-
errors=
|
|
501
|
+
errors=errors,
|
|
499
502
|
logger=logger)
|
|
500
503
|
db_close(connection=db_conn,
|
|
501
504
|
logger=logger)
|
|
502
505
|
else:
|
|
503
506
|
# refresh token not found
|
|
504
|
-
|
|
507
|
+
errors.append("Refresh token was not provided")
|
|
505
508
|
|
|
506
|
-
if
|
|
507
|
-
|
|
508
|
-
logger.error("; ".join(op_errors))
|
|
509
|
-
if isinstance(errors, list):
|
|
510
|
-
errors.extend(op_errors)
|
|
509
|
+
if errors and logger:
|
|
510
|
+
logger.error(msg="; ".join(errors))
|
|
511
511
|
|
|
512
512
|
return result
|
|
513
513
|
|
|
@@ -62,14 +62,14 @@ def provider_register(provider_id: str,
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
|
|
65
|
-
def provider_get_token(
|
|
66
|
-
|
|
65
|
+
def provider_get_token(provider_id: str,
|
|
66
|
+
errors: list[str] | None,
|
|
67
67
|
logger: Logger = None) -> str | None:
|
|
68
68
|
"""
|
|
69
69
|
Obtain an authentication token from the external provider *provider_id*.
|
|
70
70
|
|
|
71
|
-
:param errors: incidental error messages
|
|
72
71
|
:param provider_id: the provider's identification
|
|
72
|
+
:param errors: incidental error messages
|
|
73
73
|
:param logger: optional logger
|
|
74
74
|
"""
|
|
75
75
|
# initialize the return variable
|
|
@@ -6,7 +6,7 @@ from datetime import datetime, UTC
|
|
|
6
6
|
from logging import Logger
|
|
7
7
|
from pypomes_core import str_random
|
|
8
8
|
from pypomes_db import (
|
|
9
|
-
DbEngine, db_connect, db_commit, db_rollback,
|
|
9
|
+
DbEngine, db_connect, db_commit, db_rollback, db_close,
|
|
10
10
|
db_select, db_insert, db_update, db_delete
|
|
11
11
|
)
|
|
12
12
|
from threading import Lock
|
|
@@ -296,6 +296,8 @@ class JwtRegistry:
|
|
|
296
296
|
db_commit(connection=curr_conn,
|
|
297
297
|
errors=errors,
|
|
298
298
|
logger=logger)
|
|
299
|
+
db_close(connection=curr_conn)
|
|
300
|
+
|
|
299
301
|
if errors:
|
|
300
302
|
raise RuntimeError("; ".join(errors))
|
|
301
303
|
|
|
@@ -359,102 +361,100 @@ class JwtRegistry:
|
|
|
359
361
|
"""
|
|
360
362
|
from .jwt_pomes import jwt_get_claims
|
|
361
363
|
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
# noinspection PyTypeChecker
|
|
365
|
-
recs: list[tuple[int, str, str, str]] = \
|
|
366
|
-
db_select(sel_stmt=f"SELECT {JwtDbConfig.COL_KID}, {JwtDbConfig.COL_TOKEN} "
|
|
367
|
-
f"FROM {JwtDbConfig.TABLE}",
|
|
368
|
-
where_data={JwtDbConfig.COL_ACCOUNT: account_id},
|
|
369
|
-
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
370
|
-
connection=db_conn,
|
|
371
|
-
errors=errors,
|
|
372
|
-
logger=logger)
|
|
373
|
-
if errors:
|
|
374
|
-
raise RuntimeError("; ".join(errors))
|
|
364
|
+
# initialize the return variable
|
|
365
|
+
result: int | None = None
|
|
375
366
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
367
|
+
# make sure to have a database connection
|
|
368
|
+
errors: list[str] = []
|
|
369
|
+
curr_conn: Any = db_conn or db_connect(autocommit=False,
|
|
370
|
+
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
371
|
+
errors=errors,
|
|
372
|
+
logger=logger)
|
|
373
|
+
if not errors:
|
|
374
|
+
|
|
375
|
+
# retrieve the account's tokens
|
|
376
|
+
# noinspection PyTypeChecker
|
|
377
|
+
recs: list[tuple[int, str, str, str]] = \
|
|
378
|
+
db_select(sel_stmt=f"SELECT {JwtDbConfig.COL_KID}, {JwtDbConfig.COL_TOKEN} "
|
|
379
|
+
f"FROM {JwtDbConfig.TABLE}",
|
|
380
|
+
where_data={JwtDbConfig.COL_ACCOUNT: account_id},
|
|
381
|
+
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
382
|
+
connection=curr_conn,
|
|
383
|
+
errors=errors,
|
|
384
|
+
logger=logger)
|
|
385
|
+
if not errors:
|
|
386
|
+
if logger:
|
|
387
|
+
logger.debug(msg=f"Retrieved {len(recs)} tokens from storage for account '{account_id}'")
|
|
388
|
+
# remove the expired tokens
|
|
389
|
+
just_now: int = int(datetime.now(tz=UTC).timestamp())
|
|
390
|
+
oldest_ts: int = sys.maxsize
|
|
391
|
+
oldest_id: int | None = None
|
|
392
|
+
expired: list[int] = []
|
|
393
|
+
for rec in recs:
|
|
394
|
+
token: str = rec[1]
|
|
395
|
+
token_id: int = rec[0]
|
|
396
|
+
token_payload: dict[str, Any] = (jwt_get_claims(token=token,
|
|
397
|
+
errors=errors,
|
|
398
|
+
logger=logger) or {}).get("payload")
|
|
399
|
+
if errors:
|
|
400
|
+
break
|
|
401
|
+
# find expired tokens
|
|
402
|
+
exp: int = token_payload.get("exp", sys.maxsize)
|
|
403
|
+
if exp < just_now:
|
|
404
|
+
expired.append(token_id)
|
|
405
|
+
|
|
406
|
+
# find oldest token
|
|
407
|
+
iat: int = token_payload.get("iat", sys.maxsize)
|
|
408
|
+
if iat < oldest_ts:
|
|
409
|
+
oldest_ts = iat
|
|
410
|
+
oldest_id = token_id
|
|
411
|
+
|
|
412
|
+
# remove expired tokens from persistence
|
|
413
|
+
if not errors and expired:
|
|
414
|
+
db_delete(delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
415
|
+
where_data={JwtDbConfig.COL_KID: expired},
|
|
416
|
+
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
417
|
+
connection=curr_conn,
|
|
418
|
+
errors=errors,
|
|
419
|
+
logger=logger)
|
|
420
|
+
if not errors and logger:
|
|
421
|
+
logger.debug(msg=f"{len(expired)} tokens of account "
|
|
422
|
+
f"'{account_id}' removed from storage")
|
|
423
|
+
|
|
424
|
+
if not errors and 0 < JwtConfig.ACCOUNT_LIMIT.value <= len(recs) - len(expired):
|
|
425
|
+
# delete the oldest token to make way for the new one
|
|
426
|
+
db_delete(delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
|
|
427
|
+
where_data={JwtDbConfig.COL_KID: oldest_id},
|
|
428
|
+
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
429
|
+
connection=curr_conn,
|
|
430
|
+
errors=errors,
|
|
431
|
+
logger=logger)
|
|
432
|
+
if not errors and logger:
|
|
433
|
+
logger.debug(msg="Oldest active token of account "
|
|
434
|
+
f"'{account_id}' removed from storage")
|
|
435
|
+
# persist token
|
|
436
|
+
if not errors:
|
|
437
|
+
result = db_insert(insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE}",
|
|
438
|
+
insert_data={
|
|
439
|
+
JwtDbConfig.COL_ACCOUNT: account_id,
|
|
440
|
+
JwtDbConfig.COL_TOKEN: jwt_token,
|
|
441
|
+
JwtDbConfig.COL_ALGORITHM: JwtConfig.DEFAULT_ALGORITHM.value,
|
|
442
|
+
JwtDbConfig.COL_DECODER: b64encode(s=JwtConfig.DECODING_KEY.value).decode()
|
|
443
|
+
},
|
|
444
|
+
return_cols={JwtDbConfig.COL_KID: int},
|
|
445
|
+
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
446
|
+
connection=curr_conn,
|
|
447
|
+
errors=errors,
|
|
448
|
+
logger=logger)
|
|
449
|
+
# finish the operation
|
|
450
|
+
if not db_conn:
|
|
425
451
|
if errors:
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
# persist token
|
|
431
|
-
col_kid: int = db_insert(insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE}",
|
|
432
|
-
insert_data={
|
|
433
|
-
JwtDbConfig.COL_ACCOUNT: account_id,
|
|
434
|
-
JwtDbConfig.COL_TOKEN: jwt_token,
|
|
435
|
-
JwtDbConfig.COL_ALGORITHM: JwtConfig.DEFAULT_ALGORITHM.value,
|
|
436
|
-
JwtDbConfig.COL_DECODER: b64encode(s=JwtConfig.DECODING_KEY.value).decode()
|
|
437
|
-
},
|
|
438
|
-
return_cols={JwtDbConfig.COL_KID: int},
|
|
439
|
-
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
440
|
-
connection=db_conn,
|
|
441
|
-
errors=errors,
|
|
442
|
-
logger=logger)
|
|
443
|
-
if errors:
|
|
444
|
-
raise RuntimeError("; ".join(errors))
|
|
452
|
+
db_rollback(connection=curr_conn)
|
|
453
|
+
else:
|
|
454
|
+
db_commit(connection=curr_conn)
|
|
455
|
+
db_close(connection=curr_conn)
|
|
445
456
|
|
|
446
|
-
# obtain and return the token's storage id
|
|
447
|
-
reply: list[tuple[int]] = db_select(sel_stmt=f"SELECT {JwtDbConfig.COL_KID} "
|
|
448
|
-
f"FROM {JwtDbConfig.TABLE}",
|
|
449
|
-
where_data={JwtDbConfig.COL_KID: col_kid},
|
|
450
|
-
min_count=1,
|
|
451
|
-
max_count=1,
|
|
452
|
-
engine=DbEngine(JwtDbConfig.ENGINE),
|
|
453
|
-
connection=db_conn,
|
|
454
|
-
committable=False,
|
|
455
|
-
errors=errors,
|
|
456
|
-
logger=logger)
|
|
457
457
|
if errors:
|
|
458
458
|
raise RuntimeError("; ".join(errors))
|
|
459
459
|
|
|
460
|
-
return
|
|
460
|
+
return result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|