pypomes-jwt 1.1.9__tar.gz → 1.2.1__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_jwt
3
- Version: 1.1.9
3
+ Version: 1.2.1
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
@@ -10,8 +10,8 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.12
13
- Requires-Dist: cryptography>=44.0.2
13
+ Requires-Dist: cryptography>=45.0.0
14
+ Requires-Dist: flask>=3.1.1
14
15
  Requires-Dist: pyjwt>=2.10.1
15
- Requires-Dist: pypomes-core>=2.0.6
16
- Requires-Dist: pypomes-db>=2.1.5
17
- Requires-Dist: pypomes-logging>=0.6.1
16
+ Requires-Dist: pypomes-core>=2.2.9
17
+ Requires-Dist: pypomes-db>=2.2.1
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "pypomes_jwt"
9
- version = "1.1.9"
9
+ version = "1.2.1"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -19,11 +19,11 @@ classifiers = [
19
19
  "Operating System :: OS Independent"
20
20
  ]
21
21
  dependencies = [
22
+ "cryptography>=45.0.0",
23
+ "Flask>=3.1.1",
22
24
  "PyJWT>=2.10.1",
23
- "cryptography>=44.0.2",
24
- "pypomes_core>=2.0.6",
25
- "pypomes_db>=2.1.5",
26
- "pypomes_logging>=0.6.1"
25
+ "pypomes_core>=2.2.9",
26
+ "pypomes_db>=2.2.1"
27
27
  ]
28
28
 
29
29
  [project.urls]
@@ -8,7 +8,6 @@ from pypomes_db import (
8
8
  DbEngine, db_connect, db_commit,
9
9
  db_rollback, db_select, db_delete
10
10
  )
11
- from pypomes_logging import PYPOMES_LOGGER
12
11
  from typing import Any
13
12
 
14
13
  from .jwt_config import JwtConfig, JwtDbConfig
@@ -86,7 +85,7 @@ def jwt_set_account(account_id: str,
86
85
  access_max_age: int = JwtConfig.ACCESS_MAX_AGE.value,
87
86
  refresh_max_age: int = JwtConfig.REFRESH_MAX_AGE.value,
88
87
  lead_interval: int = None,
89
- logger: Logger = PYPOMES_LOGGER) -> None:
88
+ logger: Logger = None) -> None:
90
89
  """
91
90
  Establish the data needed to obtain JWT tokens for *account_id*.
92
91
 
@@ -115,7 +114,7 @@ def jwt_set_account(account_id: str,
115
114
 
116
115
 
117
116
  def jwt_remove_account(account_id: str,
118
- logger: Logger = PYPOMES_LOGGER) -> bool:
117
+ logger: Logger = None) -> bool:
119
118
  """
120
119
  Remove from storage the JWT access data for *account_id*.
121
120
 
@@ -134,7 +133,7 @@ def jwt_validate_token(errors: list[str] | None,
134
133
  token: str,
135
134
  nature: str = None,
136
135
  account_id: str = None,
137
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any] | None:
136
+ logger: Logger = None) -> dict[str, Any] | None:
138
137
  """
139
138
  Verify if *token* ia a valid JWT token.
140
139
 
@@ -259,7 +258,7 @@ def jwt_validate_token(errors: list[str] | None,
259
258
  def jwt_revoke_token(errors: list[str] | None,
260
259
  account_id: str,
261
260
  token: str,
262
- logger: Logger = PYPOMES_LOGGER) -> bool:
261
+ logger: Logger = None) -> bool:
263
262
  """
264
263
  Revoke the *refresh_token* associated with *account_id*.
265
264
 
@@ -312,7 +311,7 @@ def jwt_issue_token(errors: list[str] | None,
312
311
  duration: int,
313
312
  lead_interval: int = None,
314
313
  claims: dict[str, Any] = None,
315
- logger: Logger = PYPOMES_LOGGER) -> str:
314
+ logger: Logger = None) -> str:
316
315
  """
317
316
  Issue or refresh, and return, a JWT token associated with *account_id*, of the specified *nature*.
318
317
 
@@ -363,7 +362,7 @@ def jwt_issue_token(errors: list[str] | None,
363
362
  def jwt_issue_tokens(errors: list[str] | None,
364
363
  account_id: str,
365
364
  account_claims: dict[str, Any] = None,
366
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any]:
365
+ logger: Logger = None) -> dict[str, Any]:
367
366
  """
368
367
  Issue the JWT token pair associated with *account_id*, for access and refresh operations.
369
368
 
@@ -417,7 +416,7 @@ def jwt_issue_tokens(errors: list[str] | None,
417
416
  def jwt_refresh_tokens(errors: list[str] | None,
418
417
  account_id: str,
419
418
  refresh_token: str,
420
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any]:
419
+ logger: Logger = None) -> dict[str, Any]:
421
420
  """
422
421
  Refresh the JWT token pair associated with *account_id*, for access and refresh operations.
423
422
 
@@ -515,7 +514,7 @@ def jwt_refresh_tokens(errors: list[str] | None,
515
514
 
516
515
  def jwt_get_claims(errors: list[str] | None,
517
516
  token: str,
518
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any] | None:
517
+ logger: Logger = None) -> dict[str, Any] | None:
519
518
  """
520
519
  Retrieve and return the claims set of a JWT *token*.
521
520
 
@@ -1,7 +1,7 @@
1
1
  import jwt
2
2
  import string
3
3
  import sys
4
- from base64 import urlsafe_b64encode
4
+ from base64 import b64encode
5
5
  from datetime import datetime, timezone
6
6
  from logging import Logger
7
7
  from pypomes_core import str_random
@@ -9,7 +9,6 @@ from pypomes_db import (
9
9
  DbEngine, db_connect, db_commit, db_rollback,
10
10
  db_select, db_insert, db_update, db_delete
11
11
  )
12
- from pypomes_logging import PYPOMES_LOGGER
13
12
  from threading import Lock
14
13
  from typing import Any
15
14
 
@@ -89,7 +88,7 @@ class JwtRegistry:
89
88
  access_max_age: int,
90
89
  refresh_max_age: int,
91
90
  lead_interval: int | None,
92
- logger: Logger = PYPOMES_LOGGER) -> None:
91
+ logger: Logger = None) -> None:
93
92
  """
94
93
  Add to storage the parameters needed to produce and validate JWT tokens for *account_id*.
95
94
 
@@ -153,7 +152,7 @@ class JwtRegistry:
153
152
  duration: int,
154
153
  lead_interval: int = None,
155
154
  claims: dict[str, Any] = None,
156
- logger: Logger = PYPOMES_LOGGER) -> str:
155
+ logger: Logger = None) -> str:
157
156
  """
158
157
  Issue an return a JWT token associated with *account_id*.
159
158
 
@@ -184,8 +183,8 @@ class JwtRegistry:
184
183
  raise RuntimeError(err_msg)
185
184
 
186
185
  # obtain the account data in storage (may raise an exception)
187
- account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
188
- logger=logger)
186
+ account_data: dict[str, Any] = self.get_account_data(account_id=account_id,
187
+ logger=logger)
189
188
  # issue the token
190
189
  current_claims: dict[str, Any] = {}
191
190
  iss: str = account_data["claims"].get("iss")
@@ -213,7 +212,7 @@ class JwtRegistry:
213
212
  account_id: str,
214
213
  account_claims: dict[str, Any] = None,
215
214
  db_conn: Any = None,
216
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any]:
215
+ logger: Logger = None) -> dict[str, Any]:
217
216
  """
218
217
  Issue and return a JWT token pair associated with *account_id*.
219
218
 
@@ -240,8 +239,8 @@ class JwtRegistry:
240
239
  """
241
240
  # process the account data in storage
242
241
  with (self.access_lock):
243
- account_data: dict[str, Any] = self.__get_account_data(account_id=account_id,
244
- logger=logger)
242
+ account_data: dict[str, Any] = self.get_account_data(account_id=account_id,
243
+ logger=logger)
245
244
  current_claims: dict[str, Any] = account_data["claims"].copy()
246
245
  if account_claims:
247
246
  current_claims.update(account_claims)
@@ -271,10 +270,10 @@ class JwtRegistry:
271
270
  logger=logger)
272
271
  if curr_conn:
273
272
  # persist the candidate token (may raise an exception)
274
- token_id: int = _jwt_persist_token(account_id=account_id,
275
- jwt_token=refresh_token,
276
- db_conn=curr_conn,
277
- logger=logger)
273
+ token_id: int = JwtRegistry.jwt_persist_token(account_id=account_id,
274
+ jwt_token=refresh_token,
275
+ db_conn=curr_conn,
276
+ logger=logger)
278
277
  # issue the definitive refresh token
279
278
  refresh_token = jwt.encode(payload=current_claims,
280
279
  key=JwtConfig.ENCODING_KEY.value,
@@ -318,9 +317,9 @@ class JwtRegistry:
318
317
  "refresh-token": refresh_token
319
318
  }
320
319
 
321
- def __get_account_data(self,
322
- account_id: str,
323
- logger: Logger = PYPOMES_LOGGER) -> dict[str, Any]:
320
+ def get_account_data(self,
321
+ account_id: str,
322
+ logger: Logger = None) -> dict[str, Any]:
324
323
  """
325
324
  Retrieve the JWT access data associated with *account_id*.
326
325
 
@@ -338,138 +337,138 @@ class JwtRegistry:
338
337
 
339
338
  return result
340
339
 
340
+ @staticmethod
341
+ def jwt_persist_token(account_id: str,
342
+ jwt_token: str,
343
+ db_conn: Any,
344
+ logger: Logger = None) -> int:
345
+ """
346
+ Persist the given token, making sure that the account limit is complied with.
341
347
 
342
- def _jwt_persist_token(account_id: str,
343
- jwt_token: str,
344
- db_conn: Any,
345
- logger: Logger = PYPOMES_LOGGER) -> int:
346
- """
347
- Persist the given token, making sure that the account limit is complied with.
348
-
349
- The tokens in storage, associated with *account_id*, are examined for their expiration timestamp.
350
- If a token's expiration timestamp is in the past, it is removed from storage. If the maximum number
351
- of active tokens for *account_id* has been reached, the oldest active one is alse removed,
352
- to make room for the new *jwt_token*.
353
- The provided database connection *db_conn* indicates that this operation is part of a larger transaction.
354
-
355
- :param account_id: the account identification
356
- :param jwt_token: the JWT token to persist
357
- :param db_conn: the database connection to use
358
- :param logger: optional logger
359
- :return: the storage id of the inserted token
360
- :raises RuntimeError: error accessing the token database
361
- """
362
- from .jwt_pomes import jwt_get_claims
363
-
364
- # retrieve the account's tokens
365
- errors: list[str] = []
366
- # noinspection PyTypeChecker
367
- recs: list[tuple[int, str, str, str]] = \
368
- db_select(errors=errors,
369
- sel_stmt=f"SELECT {JwtDbConfig.COL_KID}, {JwtDbConfig.COL_TOKEN} "
370
- f"FROM {JwtDbConfig.TABLE}",
371
- where_data={JwtDbConfig.COL_ACCOUNT: account_id},
372
- engine=DbEngine(JwtDbConfig.ENGINE),
373
- connection=db_conn,
374
- committable=False,
375
- logger=logger)
376
- if errors:
377
- raise RuntimeError("; ".join(errors))
378
-
379
- if logger:
380
- logger.debug(msg=f"Read {len(recs)} token from storage for account '{account_id}'")
381
- # remove the expired tokens
382
- just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
383
- oldest_ts: int = sys.maxsize
384
- oldest_id: int | None = None
385
- existing_ids: list[int] = []
386
- expired: list[int] = []
387
- for rec in recs:
388
- token: str = rec[1]
389
- token_id: int = rec[0]
390
- token_payload: dict[str, Any] = (jwt_get_claims(errors=errors,
391
- token=token,
392
- logger=logger) or {}).get("payload")
348
+ The tokens in storage, associated with *account_id*, are examined for their expiration timestamp.
349
+ If a token's expiration timestamp is in the past, it is removed from storage. If the maximum number
350
+ of active tokens for *account_id* has been reached, the oldest active one is alse removed,
351
+ to make room for the new *jwt_token*.
352
+ The provided database connection *db_conn* indicates that this operation is part of a larger transaction.
353
+
354
+ :param account_id: the account identification
355
+ :param jwt_token: the JWT token to persist
356
+ :param db_conn: the database connection to use
357
+ :param logger: optional logger
358
+ :return: the storage id of the inserted token
359
+ :raises RuntimeError: error accessing the token database
360
+ """
361
+ from .jwt_pomes import jwt_get_claims
362
+
363
+ # retrieve the account's tokens
364
+ errors: list[str] = []
365
+ # noinspection PyTypeChecker
366
+ recs: list[tuple[int, str, str, str]] = \
367
+ db_select(errors=errors,
368
+ sel_stmt=f"SELECT {JwtDbConfig.COL_KID}, {JwtDbConfig.COL_TOKEN} "
369
+ f"FROM {JwtDbConfig.TABLE}",
370
+ where_data={JwtDbConfig.COL_ACCOUNT: account_id},
371
+ engine=DbEngine(JwtDbConfig.ENGINE),
372
+ connection=db_conn,
373
+ committable=False,
374
+ logger=logger)
393
375
  if errors:
394
376
  raise RuntimeError("; ".join(errors))
395
377
 
396
- # find expired tokens
397
- exp: int = token_payload.get("exp", sys.maxsize)
398
- if exp < just_now:
399
- expired.append(token_id)
400
-
401
- # find oldest token
402
- iat: int = token_payload.get("iat", sys.maxsize)
403
- if iat < oldest_ts:
404
- oldest_ts = iat
405
- oldest_id = token_id
406
-
407
- # save token id
408
- existing_ids.append(token_id)
409
-
410
- # remove expired tokens from persistence
411
- if expired:
412
- db_delete(errors=errors,
413
- delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
414
- where_data={JwtDbConfig.COL_KID: expired},
378
+ if logger:
379
+ logger.debug(msg=f"Read {len(recs)} token from storage for account '{account_id}'")
380
+ # remove the expired tokens
381
+ just_now: int = int(datetime.now(tz=timezone.utc).timestamp())
382
+ oldest_ts: int = sys.maxsize
383
+ oldest_id: int | None = None
384
+ existing_ids: list[int] = []
385
+ expired: list[int] = []
386
+ for rec in recs:
387
+ token: str = rec[1]
388
+ token_id: int = rec[0]
389
+ token_payload: dict[str, Any] = (jwt_get_claims(errors=errors,
390
+ token=token,
391
+ logger=logger) or {}).get("payload")
392
+ if errors:
393
+ raise RuntimeError("; ".join(errors))
394
+
395
+ # find expired tokens
396
+ exp: int = token_payload.get("exp", sys.maxsize)
397
+ if exp < just_now:
398
+ expired.append(token_id)
399
+
400
+ # find oldest token
401
+ iat: int = token_payload.get("iat", sys.maxsize)
402
+ if iat < oldest_ts:
403
+ oldest_ts = iat
404
+ oldest_id = token_id
405
+
406
+ # save token id
407
+ existing_ids.append(token_id)
408
+
409
+ # remove expired tokens from persistence
410
+ if expired:
411
+ db_delete(errors=errors,
412
+ delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
413
+ where_data={JwtDbConfig.COL_KID: expired},
414
+ engine=DbEngine(JwtDbConfig.ENGINE),
415
+ connection=db_conn,
416
+ committable=False,
417
+ logger=logger)
418
+ if errors:
419
+ raise RuntimeError("; ".join(errors))
420
+ if logger:
421
+ logger.debug(msg=f"{len(expired)} tokens of account "
422
+ f"'{account_id}' removed from storage")
423
+
424
+ if 0 < JwtConfig.ACCOUNT_LIMIT.value <= len(recs) - len(expired):
425
+ # delete the oldest token to make way for the new one
426
+ db_delete(errors=errors,
427
+ delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
428
+ where_data={JwtDbConfig.COL_KID: oldest_id},
429
+ engine=DbEngine(JwtDbConfig.ENGINE),
430
+ connection=db_conn,
431
+ committable=False,
432
+ logger=logger)
433
+ if errors:
434
+ raise RuntimeError("; ".join(errors))
435
+ if logger:
436
+ logger.debug(msg="Oldest active token of account "
437
+ f"'{account_id}' removed from storage")
438
+ # persist token
439
+ db_insert(errors=errors,
440
+ insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE}",
441
+ insert_data={
442
+ JwtDbConfig.COL_ACCOUNT: account_id,
443
+ JwtDbConfig.COL_TOKEN: jwt_token,
444
+ JwtDbConfig.COL_ALGORITHM: JwtConfig.DEFAULT_ALGORITHM.value,
445
+ JwtDbConfig.COL_DECODER: b64encode(s=JwtConfig.DECODING_KEY.value).decode()
446
+ },
415
447
  engine=DbEngine(JwtDbConfig.ENGINE),
416
448
  connection=db_conn,
417
449
  committable=False,
418
450
  logger=logger)
419
451
  if errors:
420
452
  raise RuntimeError("; ".join(errors))
421
- if logger:
422
- logger.debug(msg=f"{len(expired)} tokens of account "
423
- f"'{account_id}' removed from storage")
424
-
425
- if 0 < JwtConfig.ACCOUNT_LIMIT.value <= len(recs) - len(expired):
426
- # delete the oldest token to make way for the new one
427
- db_delete(errors=errors,
428
- delete_stmt=f"DELETE FROM {JwtDbConfig.TABLE}",
429
- where_data={JwtDbConfig.COL_KID: oldest_id},
430
- engine=DbEngine(JwtDbConfig.ENGINE),
431
- connection=db_conn,
432
- committable=False,
433
- logger=logger)
453
+
454
+ # obtain and return the token's storage id
455
+ # HAZARD: JWT_DB_COL_TOKEN's column type might prevent it for being used in a WHERE clause
456
+ where_clause: str | None = None
457
+ if existing_ids:
458
+ where_clause = f"{JwtDbConfig.COL_KID} NOT IN {existing_ids}"
459
+ where_clause = where_clause.replace("[", "(", 1).replace("]", ")", 1)
460
+ reply: list[tuple[int]] = db_select(errors=errors,
461
+ sel_stmt=f"SELECT {JwtDbConfig.COL_KID} "
462
+ f"FROM {JwtDbConfig.TABLE}",
463
+ where_clause=where_clause,
464
+ where_data={JwtDbConfig.COL_ACCOUNT: account_id},
465
+ min_count=1,
466
+ max_count=1,
467
+ engine=DbEngine(JwtDbConfig.ENGINE),
468
+ connection=db_conn,
469
+ committable=False,
470
+ logger=logger)
434
471
  if errors:
435
472
  raise RuntimeError("; ".join(errors))
436
- if logger:
437
- logger.debug(msg="Oldest active token of account "
438
- f"'{account_id}' removed from storage")
439
- # persist token
440
- db_insert(errors=errors,
441
- insert_stmt=f"INSERT INTO {JwtDbConfig.TABLE}",
442
- insert_data={
443
- JwtDbConfig.COL_ACCOUNT: account_id,
444
- JwtDbConfig.COL_TOKEN: jwt_token,
445
- JwtDbConfig.COL_ALGORITHM: JwtConfig.DEFAULT_ALGORITHM.value,
446
- JwtDbConfig.COL_DECODER: urlsafe_b64encode(s=JwtConfig.DECODING_KEY.value).decode()
447
- },
448
- engine=DbEngine(JwtDbConfig.ENGINE),
449
- connection=db_conn,
450
- committable=False,
451
- logger=logger)
452
- if errors:
453
- raise RuntimeError("; ".join(errors))
454
-
455
- # obtain and return the token's storage id
456
- # HAZARD: JWT_DB_COL_TOKEN's column type might prevent it for being used in a WHERE clause
457
- where_clause: str | None = None
458
- if existing_ids:
459
- where_clause = f"{JwtDbConfig.COL_KID} NOT IN {existing_ids}"
460
- where_clause = where_clause.replace("[", "(", 1).replace("]", ")", 1)
461
- reply: list[tuple[int]] = db_select(errors=errors,
462
- sel_stmt=f"SELECT {JwtDbConfig.COL_KID} "
463
- f"FROM {JwtDbConfig.TABLE}",
464
- where_clause=where_clause,
465
- where_data={JwtDbConfig.COL_ACCOUNT: account_id},
466
- min_count=1,
467
- max_count=1,
468
- engine=DbEngine(JwtDbConfig.ENGINE),
469
- connection=db_conn,
470
- committable=False,
471
- logger=logger)
472
- if errors:
473
- raise RuntimeError("; ".join(errors))
474
-
475
- return reply[0][0]
473
+
474
+ return reply[0][0]
File without changes
File without changes
File without changes