pypomes-iam 0.8.1__tar.gz → 0.9.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_iam
3
- Version: 0.8.1
3
+ Version: 0.9.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
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
 
7
7
  [project]
8
8
  name = "pypomes_iam"
9
- version = "0.8.1"
9
+ version = "0.9.1"
10
10
  authors = [
11
11
  { name="GT Nunes", email="wisecoder01@gmail.com" }
12
12
  ]
@@ -1,23 +1,23 @@
1
1
  from .iam_actions import (
2
2
  iam_callback, iam_exchange,
3
- iam_login, iam_logout, iam_get_token
3
+ iam_login, iam_logout, iam_get_token, iam_userinfo
4
4
  )
5
5
  from .iam_common import (
6
- IamServer, IamParam
6
+ IamServer, ServerParam
7
7
  )
8
8
  from .iam_pomes import (
9
9
  iam_setup_server, iam_setup_endpoints
10
10
  )
11
11
  from .iam_services import (
12
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
13
+ service_setup_server, service_login, service_logout,
14
+ service_get_token, service_userinfo, service_callback,
15
+ service_exchange, service_callback_exchange
17
16
  )
18
17
  from .provider_pomes import (
18
+ IamProvider, ProviderParam,
19
19
  service_get_token, provider_get_token,
20
- provider_setup_endpoint, provider_setup_logger, provider_setup_server
20
+ iam_setup_provider, provider_setup_endpoint, provider_setup_logger
21
21
  )
22
22
  from .token_pomes import (
23
23
  token_get_claims, token_get_values, token_validate
@@ -26,20 +26,20 @@ from .token_pomes import (
26
26
  __all__ = [
27
27
  # iam_actions
28
28
  "iam_callback", "iam_exchange",
29
- "iam_login", "iam_logout", "iam_get_token",
29
+ "iam_login", "iam_logout", "iam_get_token", "iam_userinfo",
30
30
  # iam_commons
31
- "IamServer", "IamParam",
31
+ "IamServer", "ServerParam",
32
32
  # iam_pomes
33
33
  "iam_setup_server", "iam_setup_endpoints",
34
34
  # iam_services
35
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",
36
+ "service_setup_server", "service_login", "service_logout",
37
+ "service_get_token", "service_userinfo", "service_callback",
38
+ "service_exchange", "service_callback_exchange",
40
39
  # provider_pomes
41
- "provider_setup_server", "provider_get_token",
42
- "provider_setup_endpoint", "provider_setup_logger", "provider_setup_server",
40
+ "IamProvider", "ProviderParam",
41
+ "service_get_token", "provider_get_token",
42
+ "iam_setup_provider", "provider_setup_endpoint", "provider_setup_logger",
43
43
  # token_pomes
44
44
  "token_get_claims", "token_get_values", "token_validate"
45
45
  ]
@@ -9,7 +9,7 @@ from pypomes_core import TZ_LOCAL, exc_format
9
9
  from typing import Any
10
10
 
11
11
  from .iam_common import (
12
- IamServer, IamParam, UserParam, _iam_lock,
12
+ IamServer, ServerParam, UserParam, _iam_lock,
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
  )
@@ -56,7 +56,7 @@ def iam_login(iam_server: IamServer,
56
56
  # ('oauth_state' is a randomly-generated string, thus 'user_data' is always a new entry)
57
57
  oauth_state: str = "".join(secrets.choice(string.ascii_letters + string.digits) for _ in range(16))
58
58
  if target_idp:
59
- oauth_state += f"#idp={target_idp}"
59
+ oauth_state += f"_{target_idp}"
60
60
 
61
61
  with _iam_lock:
62
62
  # retrieve the user data from the IAM server's registry
@@ -80,10 +80,10 @@ def iam_login(iam_server: IamServer,
80
80
  errors=errors,
81
81
  logger=logger)
82
82
  if registry:
83
- base_url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
83
+ base_url: str = f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
84
84
  result = (f"{base_url}/protocol/openid-connect/auth"
85
85
  f"?response_type=code&scope=openid"
86
- f"&client_id={registry[IamParam.CLIENT_ID]}"
86
+ f"&client_id={registry[ServerParam.CLIENT_ID]}"
87
87
  f"&redirect_uri={redirect_uri}"
88
88
  f"&state={oauth_state}")
89
89
  if target_idp:
@@ -100,9 +100,9 @@ def iam_logout(iam_server: IamServer,
100
100
  """
101
101
  Logout the user, by removing all data associating it from *iam_server*'s registry.
102
102
 
103
- The user is identified by the attribute *user-id* or "login", provided in *args*.
104
- If successful, remove all data relating to the user from the *IAM* server's registry.
105
- Otherwise, this operation fails silently, unless an error has ocurred.
103
+ The user is identified by the attribute *user-id* or *login*, provided in *args*.
104
+ A logout request is sent to *iam_server* and, if successful, remove all data relating to the user
105
+ from the *IAM* server's registry.
106
106
 
107
107
  :param iam_server: the reference registered *IAM* server
108
108
  :param args: the arguments passed when requesting the service
@@ -114,14 +114,65 @@ def iam_logout(iam_server: IamServer,
114
114
 
115
115
  if user_id:
116
116
  with _iam_lock:
117
- # retrieve the data for all users in the IAM server's registry
118
- users: dict[str, dict[str, Any]] = _get_iam_users(iam_server=iam_server,
119
- errors=errors,
120
- logger=logger) or {}
121
- if user_id in users:
122
- users.pop(user_id)
123
- if logger:
124
- logger.debug(msg=f"User '{user_id}' removed from {iam_server}'s registry")
117
+ # retrieve the IAM server's registry and the data for all users therein
118
+ registry: dict[str, Any] = _get_iam_registry(iam_server,
119
+ errors=errors,
120
+ logger=logger)
121
+ users: dict[str, dict[str, Any]] = registry[ServerParam.USERS] if registry else {}
122
+ user_data: dict[str, Any] = users.get(user_id)
123
+ if user_data:
124
+ # request the IAM server to logout 'client_id'
125
+ client_secret: str = __get_client_secret(iam_server=iam_server,
126
+ errors=errors,
127
+ logger=logger)
128
+ if client_secret:
129
+ url: str = (f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
130
+ "/protocol/openid-connect/logout")
131
+ header_data: dict[str, str] = {
132
+ "Content-Type": "application/x-www-form-urlencoded"
133
+ }
134
+ body_data: dict[str, Any] = {
135
+ "client_id": registry[ServerParam.CLIENT_ID],
136
+ "client_secret": client_secret,
137
+ "refresh_token": user_data[UserParam.REFRESH_TOKEN]
138
+ }
139
+ # log the POST
140
+ if logger:
141
+ logger.debug(msg=f"POST {url}")
142
+ try:
143
+ response: requests.Response = requests.post(url=url,
144
+ headers=header_data,
145
+ data=body_data)
146
+ if response.status_code in [200, 204]:
147
+ # request succeeded
148
+ if logger:
149
+ logger.debug(msg=f"POST success")
150
+ else:
151
+ # request failed, report the problem
152
+ msg: str = f"POST failure, status {response.status_code}, reason {response.reason}"
153
+ if logger:
154
+ logger.error(msg=msg)
155
+ if isinstance(errors, list):
156
+ errors.append(msg)
157
+ except Exception as e:
158
+ # the operation raised an exception
159
+ msg: str = exc_format(exc=e,
160
+ exc_info=sys.exc_info())
161
+ if logger:
162
+ logger.error(msg=msg)
163
+ if isinstance(errors, list):
164
+ errors.append(msg)
165
+
166
+ if not errors and user_id in users:
167
+ users.pop(user_id)
168
+ if logger:
169
+ logger.debug(msg=f"User '{user_id}' removed from {iam_server}'s registry")
170
+ else:
171
+ msg: str = "User identification not provided"
172
+ if logger:
173
+ logger.error(msg=msg)
174
+ if isinstance(errors, list):
175
+ errors.append(msg)
125
176
 
126
177
 
127
178
  def iam_get_token(iam_server: IamServer,
@@ -176,7 +227,7 @@ def iam_get_token(iam_server: IamServer,
176
227
  refresh_expiration: int = user_data[UserParam.REFRESH_EXPIRATION]
177
228
  if now < refresh_expiration:
178
229
  header_data: dict[str, str] = {
179
- "Content-Type": "application/json"
230
+ "Content-Type": "application/x-www-form-urlencoded"
180
231
  }
181
232
  body_data: dict[str, str] = {
182
233
  "grant_type": "refresh_token",
@@ -271,9 +322,9 @@ def iam_callback(iam_server: IamServer,
271
322
  if int(datetime.now(tz=TZ_LOCAL).timestamp()) > expiration:
272
323
  errors.append("Operation timeout")
273
324
  else:
274
- pos: int = oauth_state.rfind("#idp=")
275
- target_idp: str = oauth_state[pos+4:] if pos > 0 else None
276
- target_iam = IamServer(target_idp) if target_idp in IamServer else None
325
+ pos: int = oauth_state.rfind("_")
326
+ target_idp: str = oauth_state[pos+1:] if pos > 0 else None
327
+ target_iam: IamServer = IamServer(target_idp) if target_idp in IamServer else None
277
328
  target_data: dict[str, Any] = user_data.copy() if target_iam else None
278
329
  users.pop(oauth_state)
279
330
  code: str = args.get("code")
@@ -306,8 +357,8 @@ def iam_callback(iam_server: IamServer,
306
357
  registry: dict[str, Any] = _get_iam_registry(iam_server,
307
358
  errors=errors,
308
359
  logger=logger)
309
- url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
310
- url += f"/broker/{target_idp}/token"
360
+ url: str = (f"{registry[ServerParam.URL_BASE]}/realms/"
361
+ f"{registry[ServerParam.CLIENT_REALM]}/broker/{target_idp}/token")
311
362
  header_data: dict[str, str] = {
312
363
  "Authorization": f"Bearer {result[1]}",
313
364
  "Content-Type": "application/json"
@@ -375,7 +426,6 @@ def iam_exchange(iam_server: IamServer,
375
426
  errors=errors,
376
427
  logger=logger)
377
428
  if not errors:
378
- # HAZARD: only 'IAM_KEYCLOAK' is currently supported
379
429
  with _iam_lock:
380
430
  # retrieve the IAM server's registry
381
431
  registry: dict[str, Any] = _get_iam_registry(iam_server=iam_server,
@@ -401,7 +451,7 @@ def iam_exchange(iam_server: IamServer,
401
451
  "subject_token": token,
402
452
  "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
403
453
  "requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
404
- "audience": registry[IamParam.CLIENT_ID],
454
+ "audience": registry[ServerParam.CLIENT_ID],
405
455
  "subject_issuer": token_issuer
406
456
  }
407
457
  now: int = int(datetime.now(tz=TZ_LOCAL).timestamp())
@@ -429,6 +479,60 @@ def iam_exchange(iam_server: IamServer,
429
479
  return result
430
480
 
431
481
 
482
+ def iam_userinfo(iam_server: IamServer,
483
+ args: dict[str, Any],
484
+ errors: list[str] = None,
485
+ logger: Logger = None) -> dict[str, Any] | None:
486
+ """
487
+ Obtain user data from *iam_server*.
488
+
489
+ The user is identified by the attribute *user-id* or *login*, provided in *args*.
490
+
491
+ :param iam_server: the reference registered *IAM* server
492
+ :param args: the arguments passed when requesting the service
493
+ :param errors: incidental error messages
494
+ :param logger: optional logger
495
+ :return: the user information requested, or *None* if error
496
+ """
497
+ # initialize the return variable
498
+ result: dict[str, Any] | None = None
499
+
500
+ # obtain the user's identification
501
+ user_id: str = args.get("user-id") or args.get("login")
502
+
503
+ err_msg: str | None = None
504
+ if user_id:
505
+ with _iam_lock:
506
+ # retrieve the IAM server's registry and the user data therein
507
+ registry: dict[str, Any] = _get_iam_registry(iam_server,
508
+ errors=errors,
509
+ logger=logger)
510
+ user_data: dict[str, Any] = registry[ServerParam.USERS].get(user_id)
511
+ if user_data:
512
+ url: str = (f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
513
+ "/protocol/openid-connect/userinfo")
514
+ header_data: dict[str, str] = {
515
+ "Authorization": f"Bearer {args.get('access-token')}"
516
+ }
517
+ result = __get_for_data(url=url,
518
+ header_data=header_data,
519
+ params=None,
520
+ errors=errors,
521
+ logger=logger)
522
+ else:
523
+ err_msg = f"Unknown user '{user_id}'"
524
+ else:
525
+ err_msg: str = "User identification not provided"
526
+
527
+ if err_msg:
528
+ if logger:
529
+ logger.error(msg=err_msg)
530
+ if isinstance(errors, list):
531
+ errors.append(err_msg)
532
+
533
+ return result
534
+
535
+
432
536
  def __assert_link(iam_server: IamServer,
433
537
  user_id: str,
434
538
  token: str,
@@ -461,7 +565,7 @@ def __assert_link(iam_server: IamServer,
461
565
  if logger:
462
566
  logger.debug(msg="Obtaining internal identification "
463
567
  f"for user '{user_id}' in IAM server '{iam_server}'")
464
- url: str = f"{registry[IamParam.URL_BASE]}/admin/realms/{registry[IamParam.CLIENT_REALM]}/users"
568
+ url: str = f"{registry[ServerParam.URL_BASE]}/admin/realms/{registry[ServerParam.CLIENT_REALM]}/users"
465
569
  header_data: dict[str, str] = {
466
570
  "Authorization": f"Bearer {admin_token}",
467
571
  "Content-Type": "application/json"
@@ -482,8 +586,8 @@ def __assert_link(iam_server: IamServer,
482
586
  if logger:
483
587
  logger.debug(msg="Obtaining the providers federated in IAM server "
484
588
  f"'{iam_server}', for internal identification '{internal_id}'")
485
- url = (f"{registry[IamParam.URL_BASE]}/admin/realms/"
486
- f"{registry[IamParam.CLIENT_REALM]}/users/{internal_id}/federated-identity")
589
+ url = (f"{registry[ServerParam.URL_BASE]}/admin/realms/"
590
+ f"{registry[ServerParam.CLIENT_REALM]}/users/{internal_id}/federated-identity")
487
591
  providers: list[dict[str, Any]] = __get_for_data(url=url,
488
592
  header_data=header_data,
489
593
  params=None,
@@ -546,14 +650,14 @@ def __get_administrative_token(iam_server: IamServer,
546
650
  errors=errors,
547
651
  logger=logger)
548
652
  if registry:
549
- if registry[IamParam.ADMIN_ID] and registry[IamParam.ADMIN_SECRET]:
653
+ if registry[ServerParam.ADMIN_ID] and registry[ServerParam.ADMIN_SECRET]:
550
654
  header_data: dict[str, str] = {
551
655
  "Content-Type": "application/x-www-form-urlencoded"
552
656
  }
553
657
  body_data: dict[str, str] = {
554
658
  "grant_type": "password",
555
- "username": registry[IamParam.ADMIN_ID],
556
- "password": registry[IamParam.ADMIN_SECRET],
659
+ "username": registry[ServerParam.ADMIN_ID],
660
+ "password": registry[ServerParam.ADMIN_SECRET],
557
661
  "client_id": "admin-cli"
558
662
  }
559
663
  token_data: dict[str, Any] = __post_for_token(iam_server=iam_server,
@@ -569,7 +673,7 @@ def __get_administrative_token(iam_server: IamServer,
569
673
 
570
674
  elif logger or isinstance(errors, list):
571
675
  msg: str = ("Credentials for administrator of realm "
572
- f"'{registry[IamParam.CLIENT_REALM]}' "
676
+ f"'{registry[ServerParam.CLIENT_REALM]}' "
573
677
  f"at IAM server '{iam_server}' not provided")
574
678
  if logger:
575
679
  logger.error(msg=msg)
@@ -605,7 +709,7 @@ def __get_client_secret(iam_server: IamServer,
605
709
  registry: dict[str, Any] = _get_iam_registry(iam_server=iam_server,
606
710
  errors=errors,
607
711
  logger=logger)
608
- result: str = registry[IamParam.CLIENT_SECRET] if registry else None
712
+ result: str = registry[ServerParam.CLIENT_SECRET] if registry else None
609
713
 
610
714
  if not result and not errors:
611
715
  # obtain a token with administrative rights
@@ -613,13 +717,13 @@ def __get_client_secret(iam_server: IamServer,
613
717
  errors=errors,
614
718
  logger=logger)
615
719
  if token:
616
- realm: str = registry[IamParam.CLIENT_REALM]
617
- client_id: str = registry[IamParam.CLIENT_ID]
720
+ realm: str = registry[ServerParam.CLIENT_REALM]
721
+ client_id: str = registry[ServerParam.CLIENT_ID]
618
722
  if logger:
619
723
  logger.debug(msg=f"Obtaining the UUID for client '{client_id}', "
620
724
  f"in realm '{realm}' at IAM server '{iam_server}'")
621
725
  # obtain the client UUID
622
- url: str = f"{registry[IamParam.URL_BASE]}/realms/{realm}/clients"
726
+ url: str = f"{registry[ServerParam.URL_BASE]}/realms/{realm}/clients"
623
727
  header_data: dict[str, str] = {
624
728
  "Authorization": f"Bearer {token}",
625
729
  "Content-Type": "application/json"
@@ -647,7 +751,7 @@ def __get_client_secret(iam_server: IamServer,
647
751
  if reply:
648
752
  # store the client's secret password and return it
649
753
  result = reply["value"]
650
- registry[IamParam.CLIENT_ID] = result
754
+ registry[ServerParam.CLIENT_ID] = result
651
755
  return result
652
756
 
653
757
 
@@ -811,11 +915,11 @@ def __post_for_token(iam_server: IamServer,
811
915
  if registry:
812
916
  # complete the data to send in body of request
813
917
  if body_data["grant_type"] != "password":
814
- body_data["client_id"] = registry[IamParam.CLIENT_ID]
918
+ body_data["client_id"] = registry[ServerParam.CLIENT_ID]
815
919
 
816
920
  # build the URL
817
- base_url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
818
- url: str = f"{base_url}/protocol/openid-connect/token"
921
+ url: str = (f"{registry[ServerParam.URL_BASE]}/realms/"
922
+ f"{registry[ServerParam.CLIENT_REALM]}/protocol/openid-connect/token")
819
923
  # 'client_secret' data must not be shown in log
820
924
  msg: str = f"POST {url}, {json.dumps(obj=body_data,
821
925
  ensure_ascii=False)}"
@@ -916,9 +1020,9 @@ def __validate_and_store(iam_server: IamServer,
916
1020
  public_key: str = _get_public_key(iam_server=iam_server,
917
1021
  errors=errors,
918
1022
  logger=logger)
919
- recipient_attr = registry[IamParam.RECIPIENT_ATTR]
1023
+ recipient_attr = registry[ServerParam.RECIPIENT_ATTR]
920
1024
  login_id = user_data.pop("login-id", None)
921
- base_url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
1025
+ base_url: str = f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
922
1026
  claims: dict[str, dict[str, Any]] = token_validate(token=token,
923
1027
  issuer=base_url,
924
1028
  recipient_id=login_id,
@@ -1,26 +1,23 @@
1
1
  import requests
2
2
  import sys
3
3
  from datetime import datetime
4
- from enum import StrEnum, auto
4
+ from enum import StrEnum
5
5
  from logging import Logger
6
6
  from pypomes_core import (
7
7
  APP_PREFIX, TZ_LOCAL, exc_format,
8
- env_get_str, env_get_int, env_get_enums
8
+ env_get_int, env_get_str, env_get_strs
9
9
  )
10
10
  from pypomes_crypto import crypto_jwk_convert
11
11
  from threading import RLock
12
12
  from typing import Any, Final
13
13
 
14
-
15
- class IamServer(StrEnum):
16
- """
17
- Supported IAM servers.
18
- """
19
- JUSBR = auto()
20
- KEYCLOAK = auto()
14
+ _members: dict[str, str] = {key.upper(): key.lower() for key in
15
+ env_get_strs(key=f"{APP_PREFIX}_AUTH_SERVERS")}
16
+ IamServer: type[StrEnum] = StrEnum("IamServer", _members)
17
+ del _members
21
18
 
22
19
 
23
- class IamParam(StrEnum):
20
+ class ServerParam(StrEnum):
24
21
  """
25
22
  Parameters for configuring *IAM* servers.
26
23
  """
@@ -60,7 +57,7 @@ class UserParam(StrEnum):
60
57
  REDIRECT_URI = "redirect-uri"
61
58
 
62
59
 
63
- def __get_iam_data() -> dict[IamServer, dict[IamParam, Any]]:
60
+ def __get_iam_data() -> dict[IamServer, dict[ServerParam, Any]]:
64
61
  """
65
62
  Obtain the configuration data for select *IAM* servers.
66
63
 
@@ -68,9 +65,9 @@ def __get_iam_data() -> dict[IamServer, dict[IamParam, Any]]:
68
65
  or dynamically with calls to *iam_setup_server()*. Specifying configuration parameters with environment
69
66
  variables can be done by following these steps:
70
67
 
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:
68
+ 1. Specify *<APP_PREFIX>_AUTH_SERVERS* with a list of names among the values found in *IamServer* class
69
+ and the data set below for each server, where *<IAM>* stands for the server's name as presented in
70
+ *IamServer* class:
74
71
  - *<APP_PREFIX>_<IAM>_ADMIN_ID* (optional, required if administrative duties are performed)
75
72
  - *<APP_PREFIX>_<IAM>_ADMIN_PWD* (optional, required if administrative duties are performed)
76
73
  - *<APP_PREFIX>_<IAM>_CLIENT_ID* (required)
@@ -91,30 +88,29 @@ def __get_iam_data() -> dict[IamServer, dict[IamParam, Any]]:
91
88
  - *<APP_PREFIX>_<IAM>_ENDPOINT_LOGIN*
92
89
  - *<APP_PREFIX>_<IAM>_ENDPOINT_LOGOUT*
93
90
  - *<APP_PREFIX>_<IAM>_ENDPOINT_TOKEN*
91
+ - *<APP_PREFIX>_<IAM>_ENDPOINT_USERINFO*
94
92
 
95
93
  :return: the configuration data for the select *IAM* servers.
96
94
  """
97
95
  # initialize the return variable
98
- result: dict[IamServer, dict[IamParam, Any]] = {}
96
+ result: dict[IamServer, dict[ServerParam, Any]] = {}
99
97
 
100
- servers: list[IamServer] = env_get_enums(key=f"{APP_PREFIX}_IAM_SERVERS",
101
- enum_class=IamServer) or []
102
- for server in servers:
98
+ for server in IamServer:
103
99
  prefix = server.name
104
100
  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"),
101
+ ServerParam.ADMIN_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_ADMIN_ID"),
102
+ ServerParam.ADMIN_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_ADMIN_SECRET"),
103
+ ServerParam.CLIENT_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_ID"),
104
+ ServerParam.CLIENT_REALM: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_REALM"),
105
+ ServerParam.CLIENT_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_CLIENT_SECRET"),
106
+ ServerParam.LOGIN_TIMEOUT: env_get_str(key=f"{APP_PREFIX}_{prefix}_LOGIN_TIMEOUT"),
107
+ ServerParam.PK_LIFETIME: env_get_int(key=f"{APP_PREFIX}_{prefix}_PK_LIFETIME"),
108
+ ServerParam.RECIPIENT_ATTR: env_get_str(key=f"{APP_PREFIX}_{prefix}_RECIPIENT_ATTR"),
109
+ ServerParam.URL_BASE: env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_BASE"),
114
110
  # dynamically set
115
- IamParam.PK_EXPIRATION: 0,
116
- IamParam.PUBLIC_KEY: None,
117
- IamParam.USERS: {}
111
+ ServerParam.PK_EXPIRATION: 0,
112
+ ServerParam.PUBLIC_KEY: None,
113
+ ServerParam.USERS: {}
118
114
  }
119
115
 
120
116
  return result
@@ -153,7 +149,7 @@ def __get_iam_data() -> dict[IamServer, dict[IamParam, Any]]:
153
149
  # },
154
150
  # ...
155
151
  # }
156
- _IAM_SERVERS: Final[dict[IamServer, dict[IamParam, Any]]] = __get_iam_data()
152
+ _IAM_SERVERS: Final[dict[IamServer, dict[ServerParam, Any]]] = __get_iam_data()
157
153
 
158
154
 
159
155
  # the lock protecting the data in '_<IAM>_SERVERS'
@@ -173,7 +169,7 @@ def _iam_server_from_endpoint(endpoint: str,
173
169
  :return: the corresponding *IAM* server, or *None* if one could not be obtained
174
170
  """
175
171
  # initialize the return variable
176
- result: IamServer | None = None
172
+ result: type(IamServer) | None = None
177
173
 
178
174
  for iam_server in _IAM_SERVERS:
179
175
  if endpoint.startswith(iam_server):
@@ -202,10 +198,10 @@ def _iam_server_from_issuer(issuer: str,
202
198
  :return: the corresponding *IAM* server, or *None* if one could not be obtained
203
199
  """
204
200
  # initialize the return variable
205
- result: IamServer | None = None
201
+ result: type(IamServer) | None = None
206
202
 
207
203
  for iam_server, registry in _IAM_SERVERS.items():
208
- base_url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
204
+ base_url: str = f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
209
205
  if base_url == issuer:
210
206
  result = IamServer(iam_server)
211
207
  break
@@ -268,9 +264,9 @@ def _get_public_key(iam_server: IamServer,
268
264
  logger=logger)
269
265
  if registry:
270
266
  now: int = int(datetime.now(tz=TZ_LOCAL).timestamp())
271
- if now > registry[IamParam.PK_EXPIRATION]:
267
+ if now > registry[ServerParam.PK_EXPIRATION]:
272
268
  # obtain the JWKS (JSON Web Key Set) from the token issuer
273
- base_url: str = f"{registry[IamParam.URL_BASE]}/realms/{registry[IamParam.CLIENT_REALM]}"
269
+ base_url: str = f"{registry[ServerParam.URL_BASE]}/realms/{registry[ServerParam.CLIENT_REALM]}"
274
270
  url: str = f"{base_url}/protocol/openid-connect/certs"
275
271
  if logger:
276
272
  logger.debug(msg=f"Obtaining signature public key used by IAM server '{iam_server}'")
@@ -292,9 +288,9 @@ def _get_public_key(iam_server: IamServer,
292
288
  # convert from 'JWK' to 'PEM' and save it for further use
293
289
  result = crypto_jwk_convert(jwk=jwk,
294
290
  fmt="PEM")
295
- registry[IamParam.PUBLIC_KEY] = result
296
- lifetime: int = registry[IamParam.PK_LIFETIME] or 0
297
- registry[IamParam.PK_EXPIRATION] = now + lifetime if lifetime else sys.maxsize
291
+ registry[ServerParam.PUBLIC_KEY] = result
292
+ lifetime: int = registry[ServerParam.PK_LIFETIME] or 0
293
+ registry[ServerParam.PK_EXPIRATION] = now + lifetime if lifetime else sys.maxsize
298
294
  if logger:
299
295
  logger.debug("Public key obtained and saved")
300
296
  else:
@@ -319,7 +315,7 @@ def _get_public_key(iam_server: IamServer,
319
315
  if isinstance(errors, list):
320
316
  errors.append(msg)
321
317
  else:
322
- result = registry[IamParam.PUBLIC_KEY]
318
+ result = registry[ServerParam.PUBLIC_KEY]
323
319
 
324
320
  return result
325
321
 
@@ -426,4 +422,4 @@ def _get_iam_users(iam_server: IamServer,
426
422
  registry: dict[str, Any] = _get_iam_registry(iam_server=iam_server,
427
423
  errors=errors,
428
424
  logger=logger)
429
- return registry[IamParam.USERS] if registry else None
425
+ return registry[ServerParam.USERS] if registry else None