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

pypomes_iam/__init__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from .jusbr_pomes import (
2
- jusbr_setup, jusbr_get_token, jusbr_set_scope
2
+ jusbr_setup, jusbr_get_token
3
3
  )
4
4
  from .keycloak_pomes import (
5
- keycloak_setup, keycloak_get_token, keycloak_set_scope
5
+ keycloak_setup, keycloak_get_token
6
6
  )
7
7
  from .provider_pomes import (
8
8
  provider_register, provider_get_token
@@ -13,9 +13,9 @@ from .token_pomes import (
13
13
 
14
14
  __all__ = [
15
15
  # jusbr_pomes
16
- "jusbr_setup", "jusbr_get_token", "jusbr_set_scope",
16
+ "jusbr_setup", "jusbr_get_token",
17
17
  # keycloak_pomes
18
- "keycloak_setup", "keycloak_get_token", "keycloak_set_scope",
18
+ "keycloak_setup", "keycloak_get_token",
19
19
  # provider_pomes
20
20
  "provider_register", "provider_get_token",
21
21
  # token_pomes
@@ -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:
@@ -31,81 +32,11 @@ from typing import Any
31
32
  # "access-expiration": <timestamp>,
32
33
  # "login-expiration": <timestamp>, <-- transient
33
34
  # "login-id": <str>, <-- transient
34
- # "oauth-scope": <str> <-- optional
35
35
  # }
36
36
  # }
37
37
  # }
38
38
 
39
39
 
40
- def _service_callback(registry: dict[str, Any],
41
- args: dict[str, Any],
42
- errors: list[str],
43
- logger: Logger | None) -> tuple[str, str]:
44
- """
45
- Entry point for the callback from JusBR on authentication operation.
46
-
47
- :param registry: the registry holding the authentication data
48
- :param args: the arguments passed when requesting the service
49
- :param errors: incidental errors
50
- :param logger: optional logger
51
- """
52
- from .token_pomes import token_validate
53
-
54
- # initialize the return variable
55
- result: tuple[str, str] | None = None
56
-
57
- # retrieve the users authentication data
58
- cache: Cache = registry["safe-cache"]
59
- users: dict[str, dict[str, Any]] = cache.get("users")
60
-
61
- # validate the OAuth2 state
62
- oauth_state: str = args.get("state")
63
- user_data: dict[str, Any] | None = None
64
- if oauth_state:
65
- for user, data in users.items():
66
- if user == oauth_state:
67
- user_data = data
68
- break
69
-
70
- # exchange 'code' for the token
71
- if user_data:
72
- users.pop(oauth_state)
73
- code: str = args.get("code")
74
- body_data: dict[str, Any] = {
75
- "grant_type": "authorization_code",
76
- "code": code,
77
- "redirect_uri": registry.get("callback-url"),
78
- }
79
- token = _post_for_token(registry=registry,
80
- user_data=user_data,
81
- body_data=body_data,
82
- errors=errors,
83
- logger=logger)
84
- # retrieve the token's claims
85
- if not errors:
86
- public_key: bytes = _get_public_key(registry=registry,
87
- logger=logger)
88
- token_claims: dict[str, dict[str, Any]] = token_validate(token=token,
89
- issuer=registry["base-url"],
90
- public_key=public_key,
91
- errors=errors,
92
- logger=logger)
93
- if not errors:
94
- token_user: str = token_claims["payload"].get("preferred_username")
95
- if token_user == oauth_state:
96
- users[token_user] = user_data
97
- result = (token_user, token)
98
- else:
99
- errors.append(f"Token was issued to user '{token_user}'")
100
- else:
101
- msg: str = "Unknown OAuth2 code received"
102
- if _get_login_timeout(registry=registry):
103
- msg += " - possible operation timeout"
104
- errors.append(msg)
105
-
106
- return result
107
-
108
-
109
40
  def _service_login(registry: dict[str, Any],
110
41
  args: dict[str, Any],
111
42
  logger: Logger | None) -> str:
@@ -132,15 +63,11 @@ def _service_login(registry: dict[str, Any],
132
63
  user_data["login-expiration"] = int(datetime.now(tz=TZ_LOCAL).timestamp()) + timeout if timeout else None
133
64
 
134
65
  # build the redirect url
135
- result: str = (f"{registry["base-url"]}/protocol/openid-connect/auth?response_type=code"
66
+ result: str = (f"{registry["base-url"]}/protocol/openid-connect/auth"
67
+ f"?response_type=code&scope=openid"
136
68
  f"&client_id={registry["client-id"]}"
137
69
  f"&redirect_uri={registry["callback-url"]}"
138
70
  f"&state={oauth_state}")
139
- scope: str = _get_user_scope(registry=registry,
140
- user_id=user_id)
141
- if scope:
142
- user_data["oauth-scope"] = scope
143
- result += f"&scope={scope}"
144
71
 
145
72
  # logout the user
146
73
  _service_logout(registry=registry,
@@ -170,6 +97,76 @@ def _service_logout(registry: dict[str, Any],
170
97
  logger.debug(msg=f"User '{user_id}' removed from the registry")
171
98
 
172
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
+
173
170
  def _service_token(registry: dict[str, Any],
174
171
  args: dict[str, Any],
175
172
  errors: list[str] = None,
@@ -230,8 +227,6 @@ def _get_public_key(registry: dict[str, Any],
230
227
  :param registry: the registry holding the authentication data
231
228
  :return: the public key, in *DER* format
232
229
  """
233
- from pypomes_crypto import crypto_jwk_convert
234
-
235
230
  # initialize the return variable
236
231
  result: bytes | None = None
237
232
 
@@ -304,27 +299,6 @@ def _get_user_data(registry: dict[str, Any],
304
299
  return result
305
300
 
306
301
 
307
- def _get_user_scope(registry: dict[str, Any],
308
- user_id: str) -> str | None:
309
- """
310
- Retrieve the OAuth2 scope associated with *user_id*.
311
-
312
- :param registry: the registry holding the authentication data
313
- :param user_id:
314
- :return: the OAuth2 scope associated with *user_id*, or *None* if it does not exist
315
- """
316
- # initialize the return variable
317
- result: str | None = None
318
-
319
- if user_id:
320
- cache: Cache = registry["safe-cache"]
321
- users: dict[str, dict[str, Any]] = cache.get("users")
322
- if user_id in users:
323
- result = users[user_id].get("oauth2-scope")
324
-
325
- return result
326
-
327
-
328
302
  def _post_for_token(registry: dict[str, Any],
329
303
  user_data: dict[str, Any],
330
304
  body_data: dict[str, Any],
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:
@@ -7,7 +7,7 @@ from pypomes_core import (
7
7
  )
8
8
  from typing import Any, Final
9
9
 
10
- from .common_pomes import _service_token, _get_user_data
10
+ from .common_pomes import _service_token
11
11
 
12
12
  JUSBR_CLIENT_ID: Final[str] = env_get_str(key=f"{APP_PREFIX}_JUSBR_CLIENT_ID")
13
13
  JUSBR_CLIENT_SECRET: Final[str] = env_get_str(key=f"{APP_PREFIX}_JUSBR_CLIENT_SECRET")
@@ -48,7 +48,6 @@ JUSBR_URL_AUTH_CALLBACK: Final[str] = env_get_str(key=f"{APP_PREFIX}_JUSBR_URL_A
48
48
  # "access-expiration": <timestamp>,
49
49
  # "login-expiration": <timestamp>, <-- transient
50
50
  # "login-id": <str>, <-- transient
51
- # "oauth-scope": <str> <-- optional
52
51
  # }
53
52
  # }
54
53
  # }
@@ -150,23 +149,17 @@ def jusbr_get_token(user_id: str,
150
149
  logger=logger)
151
150
 
152
151
 
153
- def jusbr_set_scope(user_id: str,
154
- scope: str,
155
- logger: Logger = None) -> None:
152
+ def _jusbr_get_logger() -> Logger:
156
153
  """
157
- Set the OAuth2 scope of *user_id* to *scope*.
158
-
159
- :param user_id: the user's identification
160
- :param scope: the OAuth2 scope to set to the user
161
- :param logger: optional logger
154
+ Retrieve the logger for JusBR operations.
155
+ :return: the Keycloak logger
162
156
  """
163
- global _jusbr_registry
157
+ return _jusbr_logger
164
158
 
165
- # retrieve user data
166
- user_data: dict[str, Any] = _get_user_data(registry=_jusbr_registry,
167
- user_id=user_id,
168
- logger=logger)
169
- # set the OAuth2 scope
170
- user_data["oauth-scope"] = scope
171
- if logger:
172
- logger.debug(msg=f"Scope for user '{user_id}' set to '{scope}'")
159
+
160
+ def _jusbr_get_registry() -> dict[str, Any]:
161
+ """
162
+ Retrieve the registry holding user authentication data related to JusBR operations.
163
+ :return: the Keycloak registry
164
+ """
165
+ return _jusbr_registry
@@ -7,7 +7,7 @@ from pypomes_core import (
7
7
  )
8
8
  from typing import Any, Final
9
9
 
10
- from .common_pomes import _service_token, _get_user_data
10
+ from .common_pomes import _service_token
11
11
 
12
12
  KEYCLOAK_CLIENT_ID: Final[str] = env_get_str(key=f"{APP_PREFIX}_KEYCLOAK_CLIENT_ID")
13
13
  KEYCLOAK_CLIENT_SECRET: Final[str] = env_get_str(key=f"{APP_PREFIX}_KEYCLOAK_CLIENT_SECRET")
@@ -49,7 +49,6 @@ KEYCLOAK_URL_AUTH_CALLBACK: Final[str] = env_get_str(key=f"{APP_PREFIX}_KEYCLOAK
49
49
  # "access-expiration": <timestamp>,
50
50
  # "login-expiration": <timestamp>, <-- transient
51
51
  # "login-id": <str>, <-- transient
52
- # "oauth-scope": <str> <-- optional
53
52
  # }
54
53
  # }
55
54
  # }
@@ -153,23 +152,17 @@ def keycloak_get_token(user_id: str,
153
152
  logger=logger)
154
153
 
155
154
 
156
- def keycloak_set_scope(user_id: str,
157
- scope: str,
158
- logger: Logger | None) -> None:
155
+ def _keycloak_get_logger() -> Logger:
159
156
  """
160
- Set the OAuth2 scope of *user_id* to *scope*.
161
-
162
- :param user_id: the user's identification
163
- :param scope: the OAuth2 scope to set to the user
164
- :param logger: optional logger
157
+ Retrieve the logger for Keycloak operations.
158
+ :return: the Keycloak logger
165
159
  """
166
- global _keycloak_registry
160
+ return _keycloak_logger
167
161
 
168
- # retrieve user data
169
- user_data: dict[str, Any] = _get_user_data(registry=_keycloak_registry,
170
- user_id=user_id,
171
- logger=logger)
172
- # set the OAuth2 scope
173
- user_data["oauth-scope"] = scope
174
- if logger:
175
- logger.debug(msg=f"Scope for user '{user_id}' set to '{scope}'")
162
+
163
+ def _keycloak_get_registry() -> dict[str, Any]:
164
+ """
165
+ Retrieve the registry holding user authentication data related to Keycloak operations.
166
+ :return: the Keycloak registry
167
+ """
168
+ return _keycloak_registry
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_iam
3
- Version: 0.2.0
3
+ Version: 0.2.2
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=IveckiFrNntkuD6G_vxXld3D_bjX9Mib-tz9oj6Djwc,6647
5
+ pypomes_iam/keycloak_pomes.py,sha256=GY3o2J_hSI8T6_7WMTSSPkDY1L8ow0JjbEFLURSHa50,7056
6
+ pypomes_iam/provider_pomes.py,sha256=eP8XzjTUEpwejTkO0wmDiqKjqbIEOzRNCR2ju5E15og,5856
7
+ pypomes_iam/token_pomes.py,sha256=McjKB8omCjuicenwvDVPiWYu3-7gQeLg1AzgAVKK32M,4309
8
+ pypomes_iam-0.2.2.dist-info/METADATA,sha256=cdjTGq41Ufb0yFN0lGyWrYwp_Xz4DLyH1QGQ-w5-_8o,694
9
+ pypomes_iam-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ pypomes_iam-0.2.2.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
+ pypomes_iam-0.2.2.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- pypomes_iam/__init__.py,sha256=ieysDaKOQc3B50PvChh8DLDG5R3XgbTzX3bU0ekGoUk,760
2
- pypomes_iam/common_pomes.py,sha256=bLDaoWM5KLccxsNSyiK5UbXRNBgqsQ7TB0Q4Nc72QoI,16415
3
- pypomes_iam/iam_pomes.py,sha256=THztlEWObDY4_L8GHQem2uX5J8_44XEP-mUg2Fi_Gx0,5527
4
- pypomes_iam/jusbr_pomes.py,sha256=R-i0FatmlvTp3UszUrrz2L3BQRkZue8F9Nfy0i4cKHw,7084
5
- pypomes_iam/keycloak_pomes.py,sha256=TCye3E4xijyisgG-vKoJOhXywNgdyzTuuVzFjNbaJ3I,7490
6
- pypomes_iam/provider_pomes.py,sha256=eP8XzjTUEpwejTkO0wmDiqKjqbIEOzRNCR2ju5E15og,5856
7
- pypomes_iam/token_pomes.py,sha256=McjKB8omCjuicenwvDVPiWYu3-7gQeLg1AzgAVKK32M,4309
8
- pypomes_iam-0.2.0.dist-info/METADATA,sha256=nnSivBbIIMIyu5rSSXr5aQq8S1HhcF9xgb3WFeIx-jA,694
9
- pypomes_iam-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- pypomes_iam-0.2.0.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
- pypomes_iam-0.2.0.dist-info/RECORD,,