pypomes-iam 0.7.6__tar.gz → 0.8.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.
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/PKG-INFO +2 -2
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/pyproject.toml +2 -2
- pypomes_iam-0.8.1/src/pypomes_iam/__init__.py +49 -0
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/pypomes_iam/iam_actions.py +67 -50
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/pypomes_iam/iam_common.py +70 -29
- pypomes_iam-0.8.1/src/pypomes_iam/iam_pomes.py +169 -0
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/pypomes_iam/iam_services.py +228 -97
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/pypomes_iam/provider_pomes.py +197 -30
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/pypomes_iam/token_pomes.py +27 -0
- pypomes_iam-0.7.6/src/pypomes_iam/__init__.py +0 -39
- pypomes_iam-0.7.6/src/pypomes_iam/iam_pomes.py +0 -156
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/.gitignore +0 -0
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/LICENSE +0 -0
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/README.md +0 -0
- {pypomes_iam-0.7.6 → pypomes_iam-0.8.1}/src/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypomes_iam
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
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
|
|
@@ -12,6 +12,6 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.12
|
|
13
13
|
Requires-Dist: flask>=3.1.2
|
|
14
14
|
Requires-Dist: pyjwt>=2.10.1
|
|
15
|
-
Requires-Dist: pypomes-core>=2.8.
|
|
15
|
+
Requires-Dist: pypomes-core>=2.8.6
|
|
16
16
|
Requires-Dist: pypomes-crypto>=0.4.8
|
|
17
17
|
Requires-Dist: requests>=2.32.5
|
|
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
|
|
|
6
6
|
|
|
7
7
|
[project]
|
|
8
8
|
name = "pypomes_iam"
|
|
9
|
-
version = "0.
|
|
9
|
+
version = "0.8.1"
|
|
10
10
|
authors = [
|
|
11
11
|
{ name="GT Nunes", email="wisecoder01@gmail.com" }
|
|
12
12
|
]
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"Flask>=3.1.2",
|
|
23
23
|
"PyJWT>=2.10.1",
|
|
24
|
-
"pypomes-core>=2.8.
|
|
24
|
+
"pypomes-core>=2.8.6",
|
|
25
25
|
"pypomes-crypto>=0.4.8",
|
|
26
26
|
"requests>=2.32.5"
|
|
27
27
|
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from .iam_actions import (
|
|
2
|
+
iam_callback, iam_exchange,
|
|
3
|
+
iam_login, iam_logout, iam_get_token
|
|
4
|
+
)
|
|
5
|
+
from .iam_common import (
|
|
6
|
+
IamServer, IamParam
|
|
7
|
+
)
|
|
8
|
+
from .iam_pomes import (
|
|
9
|
+
iam_setup_server, iam_setup_endpoints
|
|
10
|
+
)
|
|
11
|
+
from .iam_services import (
|
|
12
|
+
jwt_required, iam_setup_logger,
|
|
13
|
+
service_setup_server, service_get_token,
|
|
14
|
+
service_login, service_logout,
|
|
15
|
+
service_callback, service_exchange,
|
|
16
|
+
service_callback_exchange
|
|
17
|
+
)
|
|
18
|
+
from .provider_pomes import (
|
|
19
|
+
service_get_token, provider_get_token,
|
|
20
|
+
provider_setup_endpoint, provider_setup_logger, provider_setup_server
|
|
21
|
+
)
|
|
22
|
+
from .token_pomes import (
|
|
23
|
+
token_get_claims, token_get_values, token_validate
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
# iam_actions
|
|
28
|
+
"iam_callback", "iam_exchange",
|
|
29
|
+
"iam_login", "iam_logout", "iam_get_token",
|
|
30
|
+
# iam_commons
|
|
31
|
+
"IamServer", "IamParam",
|
|
32
|
+
# iam_pomes
|
|
33
|
+
"iam_setup_server", "iam_setup_endpoints",
|
|
34
|
+
# iam_services
|
|
35
|
+
"jwt_required", "iam_setup_logger",
|
|
36
|
+
"service_setup_server", "service_get_token",
|
|
37
|
+
"service_login", "service_logout",
|
|
38
|
+
"service_callback", "service_exchange",
|
|
39
|
+
"service_callback_exchange",
|
|
40
|
+
# provider_pomes
|
|
41
|
+
"provider_setup_server", "provider_get_token",
|
|
42
|
+
"provider_setup_endpoint", "provider_setup_logger", "provider_setup_server",
|
|
43
|
+
# token_pomes
|
|
44
|
+
"token_get_claims", "token_get_values", "token_validate"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
from importlib.metadata import version
|
|
48
|
+
__version__ = version("pypomes_iam")
|
|
49
|
+
__version_info__ = tuple(int(i) for i in __version__.split(".") if i.isdigit())
|
|
@@ -13,13 +13,13 @@ from .iam_common import (
|
|
|
13
13
|
_get_iam_users, _get_iam_registry, _get_public_key,
|
|
14
14
|
_get_login_timeout, _get_user_data, _iam_server_from_issuer
|
|
15
15
|
)
|
|
16
|
-
from .token_pomes import
|
|
16
|
+
from .token_pomes import token_get_values, token_validate
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
def iam_login(iam_server: IamServer,
|
|
20
|
+
args: dict[str, Any],
|
|
21
|
+
errors: list[str] = None,
|
|
22
|
+
logger: Logger = None) -> str:
|
|
23
23
|
"""
|
|
24
24
|
Build the URL for redirecting the request to *iam_server*'s authentication page.
|
|
25
25
|
|
|
@@ -32,6 +32,11 @@ def action_login(iam_server: IamServer,
|
|
|
32
32
|
returned by *iam_server* upon login. On success, the appropriate URL for invoking
|
|
33
33
|
the IAM server's authentication page is returned.
|
|
34
34
|
|
|
35
|
+
if 'target_idp' is provided as an attribute in *args*, the OAuth2 state variable included in the
|
|
36
|
+
returned URL will be postfixed with the string *#idp=<target-idp>*. At the callback endpoint,
|
|
37
|
+
this instructs *iam_server* to act as a broker, forwading the authentication process to the
|
|
38
|
+
*IAM* server *target-idp*.
|
|
39
|
+
|
|
35
40
|
:param iam_server: the reference registered *IAM* server
|
|
36
41
|
:param args: the arguments passed when requesting the service
|
|
37
42
|
:param errors: incidental error messages
|
|
@@ -51,7 +56,7 @@ def action_login(iam_server: IamServer,
|
|
|
51
56
|
# ('oauth_state' is a randomly-generated string, thus 'user_data' is always a new entry)
|
|
52
57
|
oauth_state: str = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(16))
|
|
53
58
|
if target_idp:
|
|
54
|
-
oauth_state += f"idp={target_idp}"
|
|
59
|
+
oauth_state += f"#idp={target_idp}"
|
|
55
60
|
|
|
56
61
|
with _iam_lock:
|
|
57
62
|
# retrieve the user data from the IAM server's registry
|
|
@@ -88,10 +93,10 @@ def action_login(iam_server: IamServer,
|
|
|
88
93
|
return result
|
|
89
94
|
|
|
90
95
|
|
|
91
|
-
def
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
def iam_logout(iam_server: IamServer,
|
|
97
|
+
args: dict[str, Any],
|
|
98
|
+
errors: list[str] = None,
|
|
99
|
+
logger: Logger = None) -> None:
|
|
95
100
|
"""
|
|
96
101
|
Logout the user, by removing all data associating it from *iam_server*'s registry.
|
|
97
102
|
|
|
@@ -119,23 +124,29 @@ def action_logout(iam_server: IamServer,
|
|
|
119
124
|
logger.debug(msg=f"User '{user_id}' removed from {iam_server}'s registry")
|
|
120
125
|
|
|
121
126
|
|
|
122
|
-
def
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
def iam_get_token(iam_server: IamServer,
|
|
128
|
+
args: dict[str, Any],
|
|
129
|
+
errors: list[str] = None,
|
|
130
|
+
logger: Logger = None) -> dict[str, str]:
|
|
126
131
|
"""
|
|
127
132
|
Retrieve the authentication token for the user, from *iam_server*.
|
|
128
133
|
|
|
129
134
|
The user is identified by the attribute *user-id* or *login*, provided in *args*.
|
|
130
135
|
|
|
136
|
+
On success, the returned *dict* will contain the following JSON:
|
|
137
|
+
{
|
|
138
|
+
"access-token": <token>,
|
|
139
|
+
"user-id": <user-identification
|
|
140
|
+
}
|
|
141
|
+
|
|
131
142
|
:param iam_server: the reference registered *IAM* server
|
|
132
143
|
:param args: the arguments passed when requesting the service
|
|
133
144
|
:param errors: incidental error messages
|
|
134
145
|
:param logger: optional logger
|
|
135
|
-
:return: the
|
|
146
|
+
:return: the user identification and token issued, or *None* if error
|
|
136
147
|
"""
|
|
137
148
|
# initialize the return variable
|
|
138
|
-
result: str | None = None
|
|
149
|
+
result: dict[str, str] | None = None
|
|
139
150
|
|
|
140
151
|
# obtain the user's identification
|
|
141
152
|
user_id: str = args.get("user-id") or args.get("login")
|
|
@@ -154,7 +165,10 @@ def action_token(iam_server: IamServer,
|
|
|
154
165
|
access_expiration: int = user_data.get(UserParam.ACCESS_EXPIRATION)
|
|
155
166
|
now: int = int(datetime.now(tz=TZ_LOCAL).timestamp())
|
|
156
167
|
if now < access_expiration:
|
|
157
|
-
result =
|
|
168
|
+
result = {
|
|
169
|
+
"access-token": token,
|
|
170
|
+
"user-id": user_id
|
|
171
|
+
}
|
|
158
172
|
else:
|
|
159
173
|
# access token has expired
|
|
160
174
|
refresh_token: str = user_data[UserParam.REFRESH_TOKEN]
|
|
@@ -182,7 +196,10 @@ def action_token(iam_server: IamServer,
|
|
|
182
196
|
now=now,
|
|
183
197
|
errors=errors,
|
|
184
198
|
logger=logger)
|
|
185
|
-
result =
|
|
199
|
+
result = {
|
|
200
|
+
"access-token": token_info[1],
|
|
201
|
+
"user-id": user_id
|
|
202
|
+
}
|
|
186
203
|
else:
|
|
187
204
|
# refresh token is no longer valid
|
|
188
205
|
user_data[UserParam.REFRESH_TOKEN] = None
|
|
@@ -210,10 +227,10 @@ def action_token(iam_server: IamServer,
|
|
|
210
227
|
return result
|
|
211
228
|
|
|
212
229
|
|
|
213
|
-
def
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
230
|
+
def iam_callback(iam_server: IamServer,
|
|
231
|
+
args: dict[str, Any],
|
|
232
|
+
errors: list[str] = None,
|
|
233
|
+
logger: Logger = None) -> tuple[str, str] | None:
|
|
217
234
|
"""
|
|
218
235
|
Entry point for the callback from *iam_server* via the front-end application, on authentication operations.
|
|
219
236
|
|
|
@@ -221,6 +238,10 @@ def action_callback(iam_server: IamServer,
|
|
|
221
238
|
- *state*: used to enhance security during the authorization process, typically to provide *CSRF* protection
|
|
222
239
|
- *code*: the temporary authorization code provided by *iam_server*, to be exchanged for the token
|
|
223
240
|
|
|
241
|
+
if *state* is postfixed with the string *#idp=<target-idp>*, this instructs *iam_server* to act as a broker,
|
|
242
|
+
forwarding the authentication process to the *IAM* server *target-idp*. This mechanism fully dispenses with
|
|
243
|
+
the flows 'callback-exchange', and 'callback' followed by 'exchange'.
|
|
244
|
+
|
|
224
245
|
:param iam_server: the reference registered *IAM* server
|
|
225
246
|
:param args: the arguments passed when requesting the service
|
|
226
247
|
:param errors: incidental errors
|
|
@@ -250,7 +271,7 @@ def action_callback(iam_server: IamServer,
|
|
|
250
271
|
if int(datetime.now(tz=TZ_LOCAL).timestamp()) > expiration:
|
|
251
272
|
errors.append("Operation timeout")
|
|
252
273
|
else:
|
|
253
|
-
pos: int = oauth_state.rfind("idp=")
|
|
274
|
+
pos: int = oauth_state.rfind("#idp=")
|
|
254
275
|
target_idp: str = oauth_state[pos+4:] if pos > 0 else None
|
|
255
276
|
target_iam = IamServer(target_idp) if target_idp in IamServer else None
|
|
256
277
|
target_data: dict[str, Any] = user_data.copy() if target_iam else None
|
|
@@ -315,10 +336,10 @@ def action_callback(iam_server: IamServer,
|
|
|
315
336
|
return result
|
|
316
337
|
|
|
317
338
|
|
|
318
|
-
def
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
339
|
+
def iam_exchange(iam_server: IamServer,
|
|
340
|
+
args: dict[str, Any],
|
|
341
|
+
errors: list[str] = None,
|
|
342
|
+
logger: Logger = None) -> tuple[str, str]:
|
|
322
343
|
"""
|
|
323
344
|
Request *iam_server* to issue a token in exchange for the token obtained from another *IAM* server.
|
|
324
345
|
|
|
@@ -349,12 +370,10 @@ def action_exchange(iam_server: IamServer,
|
|
|
349
370
|
|
|
350
371
|
# obtain the token to be exchanged
|
|
351
372
|
token: str = args.get("access-token") if user_id else None
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
logger=logger) if token else None
|
|
355
|
-
token_issuer: str = _iam_server_from_issuer(issuer=token_claims["payload"]["iss"],
|
|
373
|
+
token_issuer: tuple[str] = token_get_values(token=token,
|
|
374
|
+
keys=("iss",),
|
|
356
375
|
errors=errors,
|
|
357
|
-
logger=logger)
|
|
376
|
+
logger=logger)
|
|
358
377
|
if not errors:
|
|
359
378
|
# HAZARD: only 'IAM_KEYCLOAK' is currently supported
|
|
360
379
|
with _iam_lock:
|
|
@@ -367,6 +386,7 @@ def action_exchange(iam_server: IamServer,
|
|
|
367
386
|
__assert_link(iam_server=iam_server,
|
|
368
387
|
user_id=user_id,
|
|
369
388
|
token=token,
|
|
389
|
+
token_issuer=token_issuer[0],
|
|
370
390
|
errors=errors,
|
|
371
391
|
logger=logger)
|
|
372
392
|
if not errors:
|
|
@@ -412,6 +432,7 @@ def action_exchange(iam_server: IamServer,
|
|
|
412
432
|
def __assert_link(iam_server: IamServer,
|
|
413
433
|
user_id: str,
|
|
414
434
|
token: str,
|
|
435
|
+
token_issuer: str,
|
|
415
436
|
errors: list[str] | None,
|
|
416
437
|
logger: Logger | None) -> None:
|
|
417
438
|
"""
|
|
@@ -439,7 +460,7 @@ def __assert_link(iam_server: IamServer,
|
|
|
439
460
|
# obtain the internal user identification for 'user_id'
|
|
440
461
|
if logger:
|
|
441
462
|
logger.debug(msg="Obtaining internal identification "
|
|
442
|
-
f"for user {user_id} in IAM server {iam_server}")
|
|
463
|
+
f"for user '{user_id}' in IAM server '{iam_server}'")
|
|
443
464
|
url: str = f"{registry[IamParam.URL_BASE]}/admin/realms/{registry[IamParam.CLIENT_REALM]}/users"
|
|
444
465
|
header_data: dict[str, str] = {
|
|
445
466
|
"Authorization": f"Bearer {admin_token}",
|
|
@@ -455,12 +476,12 @@ def __assert_link(iam_server: IamServer,
|
|
|
455
476
|
errors=errors,
|
|
456
477
|
logger=logger)
|
|
457
478
|
if users:
|
|
458
|
-
# verify whether the
|
|
459
|
-
#
|
|
479
|
+
# verify whether the IAM server that issued the token is a federated identity provider
|
|
480
|
+
# in the associations between 'user_id' and the internal user identification
|
|
460
481
|
internal_id: str = users[0].get("id")
|
|
461
482
|
if logger:
|
|
462
|
-
logger.debug(msg="Obtaining the providers federated
|
|
463
|
-
f"
|
|
483
|
+
logger.debug(msg="Obtaining the providers federated in IAM server "
|
|
484
|
+
f"'{iam_server}', for internal identification '{internal_id}'")
|
|
464
485
|
url = (f"{registry[IamParam.URL_BASE]}/admin/realms/"
|
|
465
486
|
f"{registry[IamParam.CLIENT_REALM]}/users/{internal_id}/federated-identity")
|
|
466
487
|
providers: list[dict[str, Any]] = __get_for_data(url=url,
|
|
@@ -469,13 +490,9 @@ def __assert_link(iam_server: IamServer,
|
|
|
469
490
|
errors=errors,
|
|
470
491
|
logger=logger)
|
|
471
492
|
no_link: bool = True
|
|
472
|
-
|
|
473
|
-
errors=errors,
|
|
474
|
-
logger=logger)
|
|
475
|
-
issuer: str = claims["payload"]["iss"] if claims else None
|
|
476
|
-
provider_name: str = _iam_server_from_issuer(issuer=issuer,
|
|
493
|
+
provider_name: str = _iam_server_from_issuer(issuer=token_issuer,
|
|
477
494
|
errors=errors,
|
|
478
|
-
logger=logger)
|
|
495
|
+
logger=logger)
|
|
479
496
|
if provider_name:
|
|
480
497
|
for provider in providers:
|
|
481
498
|
if provider.get("identityProvider") == provider_name:
|
|
@@ -483,17 +500,17 @@ def __assert_link(iam_server: IamServer,
|
|
|
483
500
|
break
|
|
484
501
|
if no_link:
|
|
485
502
|
# link the identities
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
503
|
+
token_sub: tuple[str] = token_get_values(token=token,
|
|
504
|
+
keys=("sub",),
|
|
505
|
+
errors=errors,
|
|
506
|
+
logger=logger)
|
|
507
|
+
if token_sub:
|
|
491
508
|
if logger:
|
|
492
509
|
logger.debug(msg="Creating an association between identifications "
|
|
493
|
-
f"'{user_id}' and '{token_sub}' in IAM server {iam_server}")
|
|
510
|
+
f"'{user_id}' and '{token_sub}' in IAM server '{iam_server}'")
|
|
494
511
|
url += f"/{provider_name}"
|
|
495
512
|
json_data: dict[str, Any] = {
|
|
496
|
-
"userId": token_sub,
|
|
513
|
+
"userId": token_sub[0],
|
|
497
514
|
"userName": user_id
|
|
498
515
|
}
|
|
499
516
|
__post_json(url=url,
|
|
@@ -3,7 +3,10 @@ import sys
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from enum import StrEnum, auto
|
|
5
5
|
from logging import Logger
|
|
6
|
-
from pypomes_core import
|
|
6
|
+
from pypomes_core import (
|
|
7
|
+
APP_PREFIX, TZ_LOCAL, exc_format,
|
|
8
|
+
env_get_str, env_get_int, env_get_enums
|
|
9
|
+
)
|
|
7
10
|
from pypomes_crypto import crypto_jwk_convert
|
|
8
11
|
from threading import RLock
|
|
9
12
|
from typing import Any, Final
|
|
@@ -21,12 +24,14 @@ class IamParam(StrEnum):
|
|
|
21
24
|
"""
|
|
22
25
|
Parameters for configuring *IAM* servers.
|
|
23
26
|
"""
|
|
27
|
+
|
|
24
28
|
ADMIN_ID = "admin-id"
|
|
25
29
|
ADMIN_SECRET = "admin-secret"
|
|
26
30
|
CLIENT_ID = "client-id"
|
|
27
31
|
CLIENT_REALM = "client-realm"
|
|
28
32
|
CLIENT_SECRET = "client-secret"
|
|
29
33
|
ENDPOINT_CALLBACK = "endpoint-callback"
|
|
34
|
+
ENDPOINT_CALLBACK_EXCHANGE = "endpoint-callback-exchange"
|
|
30
35
|
ENDPOINT_LOGIN = "endpoint-login"
|
|
31
36
|
ENDPOINT_LOGOUT = "endpoint_logout"
|
|
32
37
|
ENDPOINT_TOKEN = "endpoint-token"
|
|
@@ -34,8 +39,9 @@ class IamParam(StrEnum):
|
|
|
34
39
|
LOGIN_TIMEOUT = "login-timeout"
|
|
35
40
|
PK_EXPIRATION = "pk-expiration"
|
|
36
41
|
PK_LIFETIME = "pk-lifetime"
|
|
37
|
-
PUBLIC_KEY = "public-key"
|
|
38
42
|
RECIPIENT_ATTR = "recipient-attr"
|
|
43
|
+
# dynamic attributes
|
|
44
|
+
PUBLIC_KEY = "public-key"
|
|
39
45
|
URL_BASE = "url-base"
|
|
40
46
|
USERS = "users"
|
|
41
47
|
|
|
@@ -54,31 +60,65 @@ class UserParam(StrEnum):
|
|
|
54
60
|
REDIRECT_URI = "redirect-uri"
|
|
55
61
|
|
|
56
62
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
63
|
+
def __get_iam_data() -> dict[IamServer, dict[IamParam, Any]]:
|
|
64
|
+
"""
|
|
65
|
+
Obtain the configuration data for select *IAM* servers.
|
|
66
|
+
|
|
67
|
+
The configuration parameters for the IAM servers are specified dynamically with environment variables,
|
|
68
|
+
or dynamically with calls to *iam_setup_server()*. Specifying configuration parameters with environment
|
|
69
|
+
variables can be done by following these steps:
|
|
70
|
+
|
|
71
|
+
1. Specify *<APP_PREFIX>_IAM_SERVERS* with a list of names among the values found in *IamServer* class
|
|
72
|
+
(currently, *jusbr* and *keycloak* are supported), and the data set below for each server, where
|
|
73
|
+
*<IAM>* stands for the server's name as presented in *IamServer* class:
|
|
74
|
+
- *<APP_PREFIX>_<IAM>_ADMIN_ID* (optional, required if administrative duties are performed)
|
|
75
|
+
- *<APP_PREFIX>_<IAM>_ADMIN_PWD* (optional, required if administrative duties are performed)
|
|
76
|
+
- *<APP_PREFIX>_<IAM>_CLIENT_ID* (required)
|
|
77
|
+
- *<APP_PREFIX>_<IAM>_CLIENT_REALM* (required)
|
|
78
|
+
- *<APP_PREFIX>_<IAM>_CLIENT_SECRET* (required)
|
|
79
|
+
- *<APP_PREFIX>_<IAM>_LOGIN_TIMEOUT* (optional, defaults to no timeout)
|
|
80
|
+
- *<APP_PREFIX>_<IAM>_PK_LIFETIME* (optional, defaults to non-terminating lifetime)
|
|
81
|
+
- *<APP_PREFIX>_<IAM>_RECIPIENT_ATTR* (required)
|
|
82
|
+
- *<APP_PREFIX>_<IAM>_URL_BASE* (required)
|
|
83
|
+
|
|
84
|
+
2. A group of special environment variables identifying endpoints for authentication services may be specified,
|
|
85
|
+
following the same scheme as presented in item *1* above. These are not part of the *IAM* server's setup,
|
|
86
|
+
but are meant to be used by function *iam_setup_endpoints()*, wherein the values in those variables
|
|
87
|
+
would represent default values for its parameters, respectively:
|
|
88
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_CALLBACK*
|
|
89
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_CALLBACK_EXCHANGE*
|
|
90
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_EXCHANGE*
|
|
91
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_LOGIN*
|
|
92
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_LOGOUT*
|
|
93
|
+
- *<APP_PREFIX>_<IAM>_ENDPOINT_TOKEN*
|
|
94
|
+
|
|
95
|
+
:return: the configuration data for the select *IAM* servers.
|
|
96
|
+
"""
|
|
97
|
+
# initialize the return variable
|
|
98
|
+
result: dict[IamServer, dict[IamParam, Any]] = {}
|
|
99
|
+
|
|
100
|
+
servers: list[IamServer] = env_get_enums(key=f"{APP_PREFIX}_IAM_SERVERS",
|
|
101
|
+
enum_class=IamServer) or []
|
|
102
|
+
for server in servers:
|
|
103
|
+
prefix = server.name
|
|
104
|
+
result[server] = {
|
|
105
|
+
IamParam.ADMIN_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_ADMIN_ID"),
|
|
106
|
+
IamParam.ADMIN_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_ADMIN_SECRET"),
|
|
107
|
+
IamParam.CLIENT_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_ID"),
|
|
108
|
+
IamParam.CLIENT_REALM: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_REALM"),
|
|
109
|
+
IamParam.CLIENT_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_SECRET"),
|
|
110
|
+
IamParam.LOGIN_TIMEOUT: env_get_str(key=f"{APP_PREFIX}_{prefix}_LOGIN_TIMEOUT"),
|
|
111
|
+
IamParam.PK_LIFETIME: env_get_int(key=f"{APP_PREFIX}_{prefix}_PUBLIC_KEY_LIFETIME"),
|
|
112
|
+
IamParam.RECIPIENT_ATTR: env_get_str(key=f"{APP_PREFIX}_{prefix}_RECIPIENT_ATTR"),
|
|
113
|
+
IamParam.URL_BASE: env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_AUTH_BASE"),
|
|
114
|
+
# dynamically set
|
|
115
|
+
IamParam.PK_EXPIRATION: 0,
|
|
116
|
+
IamParam.PUBLIC_KEY: None,
|
|
117
|
+
IamParam.USERS: {}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
82
122
|
|
|
83
123
|
# registry structure:
|
|
84
124
|
# { <IamServer>:
|
|
@@ -91,6 +131,7 @@ class UserParam(StrEnum):
|
|
|
91
131
|
# "client-realm": <str,
|
|
92
132
|
# "client-timeout": <int>,
|
|
93
133
|
# "recipient-attr": <str>,
|
|
134
|
+
# # dynamic attributes
|
|
94
135
|
# "public-key": <str>,
|
|
95
136
|
# "pk-lifetime": <int>,
|
|
96
137
|
# "pk-expiration": <int>,
|
|
@@ -112,10 +153,10 @@ class UserParam(StrEnum):
|
|
|
112
153
|
# },
|
|
113
154
|
# ...
|
|
114
155
|
# }
|
|
115
|
-
_IAM_SERVERS: Final[dict[IamServer, dict[IamParam, Any]]] =
|
|
156
|
+
_IAM_SERVERS: Final[dict[IamServer, dict[IamParam, Any]]] = __get_iam_data()
|
|
116
157
|
|
|
117
158
|
|
|
118
|
-
# the lock protecting the data in '
|
|
159
|
+
# the lock protecting the data in '_<IAM>_SERVERS'
|
|
119
160
|
# (because it is 'Final' and set at declaration time, it can be accessed through simple imports)
|
|
120
161
|
_iam_lock: Final[RLock] = RLock()
|
|
121
162
|
|