pypomes-iam 0.2.1__py3-none-any.whl → 0.2.3__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.

Potentially problematic release.


This version of pypomes-iam might be problematic. Click here for more details.

@@ -8,6 +8,7 @@ from datetime import datetime
8
8
  from flask import Request
9
9
  from logging import Logger
10
10
  from pypomes_core import TZ_LOCAL, exc_format
11
+ from pypomes_crypto import crypto_jwk_convert
11
12
  from typing import Any
12
13
 
13
14
  # registry structure:
@@ -36,75 +37,6 @@ from typing import Any
36
37
  # }
37
38
 
38
39
 
39
- def _service_callback(registry: dict[str, Any],
40
- args: dict[str, Any],
41
- errors: list[str],
42
- logger: Logger | None) -> tuple[str, str]:
43
- """
44
- Entry point for the callback from JusBR on authentication operation.
45
-
46
- :param registry: the registry holding the authentication data
47
- :param args: the arguments passed when requesting the service
48
- :param errors: incidental errors
49
- :param logger: optional logger
50
- """
51
- from .token_pomes import token_validate
52
-
53
- # initialize the return variable
54
- result: tuple[str, str] | None = None
55
-
56
- # retrieve the users authentication data
57
- cache: Cache = registry["safe-cache"]
58
- users: dict[str, dict[str, Any]] = cache.get("users")
59
-
60
- # validate the OAuth2 state
61
- oauth_state: str = args.get("state")
62
- user_data: dict[str, Any] | None = None
63
- if oauth_state:
64
- for user, data in users.items():
65
- if user == oauth_state:
66
- user_data = data
67
- break
68
-
69
- # exchange 'code' for the token
70
- if user_data:
71
- users.pop(oauth_state)
72
- code: str = args.get("code")
73
- body_data: dict[str, Any] = {
74
- "grant_type": "authorization_code",
75
- "code": code,
76
- "redirect_uri": registry.get("callback-url"),
77
- }
78
- token = _post_for_token(registry=registry,
79
- user_data=user_data,
80
- body_data=body_data,
81
- errors=errors,
82
- logger=logger)
83
- # retrieve the token's claims
84
- if not errors:
85
- public_key: bytes = _get_public_key(registry=registry,
86
- logger=logger)
87
- token_claims: dict[str, dict[str, Any]] = token_validate(token=token,
88
- issuer=registry["base-url"],
89
- public_key=public_key,
90
- errors=errors,
91
- logger=logger)
92
- if not errors:
93
- token_user: str = token_claims["payload"].get("preferred_username")
94
- if token_user == oauth_state:
95
- users[token_user] = user_data
96
- result = (token_user, token)
97
- else:
98
- errors.append(f"Token was issued to user '{token_user}'")
99
- else:
100
- msg: str = "Unknown OAuth2 code received"
101
- if _get_login_timeout(registry=registry):
102
- msg += " - possible operation timeout"
103
- errors.append(msg)
104
-
105
- return result
106
-
107
-
108
40
  def _service_login(registry: dict[str, Any],
109
41
  args: dict[str, Any],
110
42
  logger: Logger | None) -> str:
@@ -165,6 +97,76 @@ def _service_logout(registry: dict[str, Any],
165
97
  logger.debug(msg=f"User '{user_id}' removed from the registry")
166
98
 
167
99
 
100
+ def _service_callback(registry: dict[str, Any],
101
+ args: dict[str, Any],
102
+ errors: list[str],
103
+ logger: Logger | None) -> tuple[str, str]:
104
+ """
105
+ Entry point for the callback from JusBR on authentication operation.
106
+
107
+ :param registry: the registry holding the authentication data
108
+ :param args: the arguments passed when requesting the service
109
+ :param errors: incidental errors
110
+ :param logger: optional logger
111
+ """
112
+ from .token_pomes import token_validate
113
+
114
+ # initialize the return variable
115
+ result: tuple[str, str] | None = None
116
+
117
+ # retrieve the users authentication data
118
+ cache: Cache = registry["safe-cache"]
119
+ users: dict[str, dict[str, Any]] = cache.get("users")
120
+
121
+ # validate the OAuth2 state
122
+ oauth_state: str = args.get("state")
123
+ user_data: dict[str, Any] | None = None
124
+ if oauth_state:
125
+ for user, data in users.items():
126
+ if user == oauth_state:
127
+ user_data = data
128
+ break
129
+
130
+ # exchange 'code' for the token
131
+ if user_data:
132
+ expiration: int = user_data["login-expiration"] or sys.maxsize
133
+ if int(datetime.now(tz=TZ_LOCAL).timestamp()) > expiration:
134
+ errors.append("Operation timeout")
135
+ else:
136
+ users.pop(oauth_state)
137
+ code: str = args.get("code")
138
+ body_data: dict[str, Any] = {
139
+ "grant_type": "authorization_code",
140
+ "code": code,
141
+ "redirect_uri": registry.get("callback-url"),
142
+ }
143
+ token = _post_for_token(registry=registry,
144
+ user_data=user_data,
145
+ body_data=body_data,
146
+ errors=errors,
147
+ logger=logger)
148
+ # retrieve the token's claims
149
+ if not errors:
150
+ public_key: bytes = _get_public_key(registry=registry,
151
+ logger=logger)
152
+ token_claims: dict[str, dict[str, Any]] = token_validate(token=token,
153
+ issuer=registry["base-url"],
154
+ public_key=public_key,
155
+ errors=errors,
156
+ logger=logger)
157
+ if not errors:
158
+ token_user: str = token_claims["payload"].get("preferred_username")
159
+ if token_user == oauth_state:
160
+ users[token_user] = user_data
161
+ result = (token_user, token)
162
+ else:
163
+ errors.append(f"Token was issued to user '{token_user}'")
164
+ else:
165
+ errors.append("Unknown state received")
166
+
167
+ return result
168
+
169
+
168
170
  def _service_token(registry: dict[str, Any],
169
171
  args: dict[str, Any],
170
172
  errors: list[str] = None,
@@ -225,8 +227,6 @@ def _get_public_key(registry: dict[str, Any],
225
227
  :param registry: the registry holding the authentication data
226
228
  :return: the public key, in *DER* format
227
229
  """
228
- from pypomes_crypto import crypto_jwk_convert
229
-
230
230
  # initialize the return variable
231
231
  result: bytes | None = None
232
232
 
pypomes_iam/iam_pomes.py CHANGED
@@ -6,8 +6,8 @@ from .common_pomes import (
6
6
  _service_login, _service_logout,
7
7
  _service_callback, _service_token, _log_init
8
8
  )
9
- from .jusbr_pomes import _jusbr_logger, _jusbr_registry
10
- from .keycloak_pomes import _keycloak_logger, _keycloak_registry
9
+ from .jusbr_pomes import _jusbr_get_logger, _jusbr_get_registry
10
+ from .keycloak_pomes import _keycloak_get_logger, _keycloak_get_registry
11
11
 
12
12
 
13
13
  # @flask_app.route(rule=<login_endpoint>, # JUSBR_LOGIN_ENDPOINT: /iam/jusbr:login
@@ -25,11 +25,11 @@ def service_login() -> Response:
25
25
  logger: Logger
26
26
  registry: dict[str, Any]
27
27
  if request.endpoint == "jusbr-login":
28
- logger = _jusbr_logger
29
- registry = _jusbr_registry
28
+ logger = _jusbr_get_logger()
29
+ registry = _jusbr_get_registry()
30
30
  else:
31
- logger = _keycloak_logger
32
- registry = _keycloak_registry
31
+ logger = _keycloak_get_logger()
32
+ registry = _keycloak_get_registry()
33
33
 
34
34
  # log the request
35
35
  if logger:
@@ -64,11 +64,11 @@ def service_logout() -> Response:
64
64
  logger: Logger
65
65
  registry: dict[str, Any]
66
66
  if request.endpoint == "jusbr-logout":
67
- logger = _jusbr_logger
68
- registry = _jusbr_registry
67
+ logger = _jusbr_get_logger()
68
+ registry = _jusbr_get_registry()
69
69
  else:
70
- logger = _keycloak_logger
71
- registry = _keycloak_registry
70
+ logger = _keycloak_get_logger()
71
+ registry = _keycloak_get_registry()
72
72
 
73
73
  # log the request
74
74
  if logger:
@@ -101,11 +101,11 @@ def service_callback() -> Response:
101
101
  logger: Logger
102
102
  registry: dict[str, Any]
103
103
  if request.endpoint == "jusbr-callback":
104
- logger = _jusbr_logger
105
- registry = _jusbr_registry
104
+ logger = _jusbr_get_logger()
105
+ registry = _jusbr_get_registry()
106
106
  else:
107
- logger = _keycloak_logger
108
- registry = _keycloak_registry
107
+ logger = _keycloak_get_logger()
108
+ registry = _keycloak_get_registry()
109
109
 
110
110
  # log the request
111
111
  if logger:
@@ -146,11 +146,11 @@ def service_token() -> Response:
146
146
  logger: Logger
147
147
  registry: dict[str, Any]
148
148
  if request.endpoint == "jusbr-token":
149
- logger = _jusbr_logger
150
- registry = _jusbr_registry
149
+ logger = _jusbr_get_logger()
150
+ registry = _jusbr_get_registry()
151
151
  else:
152
- logger = _keycloak_logger
153
- registry = _keycloak_registry
152
+ logger = _keycloak_get_logger()
153
+ registry = _keycloak_get_registry()
154
154
 
155
155
  # log the request
156
156
  if logger:
@@ -1,4 +1,4 @@
1
- from cachetools import FIFOCache
1
+ from cachetools import Cache, FIFOCache
2
2
  from datetime import datetime
3
3
  from flask import Flask
4
4
  from logging import Logger
@@ -37,9 +37,9 @@ JUSBR_URL_AUTH_CALLBACK: Final[str] = env_get_str(key=f"{APP_PREFIX}_JUSBR_URL_A
37
37
  # "key-expiration": <int>,
38
38
  # "base-url": <str>,
39
39
  # "callback-url": <str>,
40
- # "cache-obj": <FIFOCache>
40
+ # "safe-cache": <FIFOCache>
41
41
  # }
42
- # data in "cache-obj":
42
+ # data in "safe-cache":
43
43
  # {
44
44
  # "users": {
45
45
  # "<user-id>": {
@@ -94,6 +94,8 @@ def jusbr_setup(flask_app: Flask,
94
94
  _logger = logger
95
95
 
96
96
  # configure the JusBR registry
97
+ safe_cache: Cache = FIFOCache(maxsize=1048576)
98
+ safe_cache["users"] = {}
97
99
  _jusbr_registry = {
98
100
  "client-id": client_id,
99
101
  "client-secret": client_secret,
@@ -102,7 +104,7 @@ def jusbr_setup(flask_app: Flask,
102
104
  "callback-url": callback_url,
103
105
  "key-expiration": int(datetime.now(tz=TZ_LOCAL).timestamp()),
104
106
  "key-lifetime": public_key_lifetime,
105
- "cache-obj": FIFOCache(maxsize=1048576)
107
+ "safe-cache": safe_cache
106
108
  }
107
109
 
108
110
  # establish the endpoints
@@ -147,3 +149,19 @@ def jusbr_get_token(user_id: str,
147
149
  args=args,
148
150
  errors=errors,
149
151
  logger=logger)
152
+
153
+
154
+ def _jusbr_get_logger() -> Logger:
155
+ """
156
+ Retrieve the logger for JusBR operations.
157
+ :return: the Keycloak logger
158
+ """
159
+ return _jusbr_logger
160
+
161
+
162
+ def _jusbr_get_registry() -> dict[str, Any]:
163
+ """
164
+ Retrieve the registry holding user authentication data related to JusBR operations.
165
+ :return: the Keycloak registry
166
+ """
167
+ return _jusbr_registry
@@ -1,4 +1,4 @@
1
- from cachetools import FIFOCache
1
+ from cachetools import Cache, FIFOCache
2
2
  from datetime import datetime
3
3
  from flask import Flask
4
4
  from logging import Logger
@@ -97,6 +97,8 @@ def keycloak_setup(flask_app: Flask,
97
97
  _keycloak_logger = logger
98
98
 
99
99
  # configure the JusBR registry
100
+ safe_cache: Cache = FIFOCache(maxsize=1048576)
101
+ safe_cache["users"] = {}
100
102
  _keycloak_registry = {
101
103
  "client-id": client_id,
102
104
  "client-secret": client_secret,
@@ -105,7 +107,7 @@ def keycloak_setup(flask_app: Flask,
105
107
  "callback-url": callback_url,
106
108
  "key-expiration": int(datetime.now(tz=TZ_LOCAL).timestamp()),
107
109
  "key-lifetime": public_key_lifetime,
108
- "safe-cache": FIFOCache(maxsize=1048576)
110
+ "safe-cache": safe_cache
109
111
  }
110
112
 
111
113
  # establish the endpoints
@@ -150,3 +152,19 @@ def keycloak_get_token(user_id: str,
150
152
  args=args,
151
153
  errors=errors,
152
154
  logger=logger)
155
+
156
+
157
+ def _keycloak_get_logger() -> Logger:
158
+ """
159
+ Retrieve the logger for Keycloak operations.
160
+ :return: the Keycloak logger
161
+ """
162
+ return _keycloak_logger
163
+
164
+
165
+ def _keycloak_get_registry() -> dict[str, Any]:
166
+ """
167
+ Retrieve the registry holding user authentication data related to Keycloak operations.
168
+ :return: the Keycloak registry
169
+ """
170
+ return _keycloak_registry
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_iam
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: A collection of Python pomes, penyeach (IAM modules)
5
5
  Project-URL: Homepage, https://github.com/TheWiseCoder/PyPomes-IAM
6
6
  Project-URL: Bug Tracker, https://github.com/TheWiseCoder/PyPomes-IAM/issues
@@ -0,0 +1,11 @@
1
+ pypomes_iam/__init__.py,sha256=u-gNGbsayMf-2SWTB8VcoTCADoczZuwNEH50BPxTZZ8,682
2
+ pypomes_iam/common_pomes.py,sha256=08G8-Rcpuld4JxciEEhzORMt575kErHGEVk8_QC1uC4,15710
3
+ pypomes_iam/iam_pomes.py,sha256=-2XppbAAuY58jYKd4ZN2MMjQd7wh6n9I8cofgAt0_9s,5639
4
+ pypomes_iam/jusbr_pomes.py,sha256=FFTzhpxB4Y7T_JKYC1xvYIcb-ca-Frtl-7LK7dDkIXQ,6723
5
+ pypomes_iam/keycloak_pomes.py,sha256=_hR1nZhk-ejZpYrwijIcEm_GyfrtQkWtNdqJI7hcZuY,7129
6
+ pypomes_iam/provider_pomes.py,sha256=eP8XzjTUEpwejTkO0wmDiqKjqbIEOzRNCR2ju5E15og,5856
7
+ pypomes_iam/token_pomes.py,sha256=McjKB8omCjuicenwvDVPiWYu3-7gQeLg1AzgAVKK32M,4309
8
+ pypomes_iam-0.2.3.dist-info/METADATA,sha256=IzheEi4k7zb4WSewyRmLbQvJ83kKZq1OfqL7TlVEp3s,694
9
+ pypomes_iam-0.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ pypomes_iam-0.2.3.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
+ pypomes_iam-0.2.3.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- pypomes_iam/__init__.py,sha256=u-gNGbsayMf-2SWTB8VcoTCADoczZuwNEH50BPxTZZ8,682
2
- pypomes_iam/common_pomes.py,sha256=wzyXM0lK7eXuUaGow88872wxbYpJ8DcXjVAVKZnTkmU,15533
3
- pypomes_iam/iam_pomes.py,sha256=THztlEWObDY4_L8GHQem2uX5J8_44XEP-mUg2Fi_Gx0,5527
4
- pypomes_iam/jusbr_pomes.py,sha256=ioJSZhuPR5xMzomT2zomNH90uIdbwT6GoracIClgsh0,6261
5
- pypomes_iam/keycloak_pomes.py,sha256=UpHSr4bhNdDl7sr8GSCIDTdPsXEgX3N11qOiR48TvjA,6652
6
- pypomes_iam/provider_pomes.py,sha256=eP8XzjTUEpwejTkO0wmDiqKjqbIEOzRNCR2ju5E15og,5856
7
- pypomes_iam/token_pomes.py,sha256=McjKB8omCjuicenwvDVPiWYu3-7gQeLg1AzgAVKK32M,4309
8
- pypomes_iam-0.2.1.dist-info/METADATA,sha256=XkLYRIQ6OK6TUGH2SKUsrWIG_zwZHQ9tm0ud_YEEef0,694
9
- pypomes_iam-0.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- pypomes_iam-0.2.1.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
- pypomes_iam-0.2.1.dist-info/RECORD,,