pypomes-iam 0.8.0__py3-none-any.whl → 0.8.9__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.
@@ -14,6 +14,11 @@ from pypomes_core import (
14
14
  from threading import Lock
15
15
  from typing import Any, Final
16
16
 
17
+ _members: dict[str, str] = {key.upper(): key.lower() for key in
18
+ env_get_strs(key=f"{APP_PREFIX}_AUTH_PROVIDERS")}
19
+ IamProvider: type[StrEnum] = StrEnum("IamProvider", _members)
20
+ del _members
21
+
17
22
 
18
23
  class ProviderParam(StrEnum):
19
24
  """
@@ -28,7 +33,7 @@ class ProviderParam(StrEnum):
28
33
  ACCESS_EXPIRATION = "access-expiration"
29
34
  REFRESH_TOKEN = "refresh-token"
30
35
  REFRESH_EXPIRATION = "refresh-expiration"
31
- URL_AUTH = "url-auth"
36
+ URL_TOKEN = "url-token"
32
37
 
33
38
 
34
39
  # the logger for IAM service operations
@@ -36,43 +41,42 @@ class ProviderParam(StrEnum):
36
41
  __JWT_LOGGER: Logger | None = None
37
42
 
38
43
 
39
- def __get_provider_data() -> dict[str, dict[ProviderParam, Any]]:
44
+ def __get_provider_data() -> dict[IamProvider, dict[ProviderParam, Any]]:
40
45
  """
41
- Obtain the configuration data for select *JWT* providers.
46
+ Obtain the configuration data for select *IAM* providers.
42
47
 
43
- The configuration parameters for the JWT providers are specified with environment variables,
48
+ The configuration parameters for the *IAM* providers are specified with environment variables,
44
49
  or dynamically with *provider_setup_server()*. Specifying configuration parameters with
45
50
  environment variables can be done by following these steps:
46
51
 
47
- 1. Specify *<APP_PREFIX>_IAM_PROVIDERS* with a list of names (typically, in lower-case), and the data set
48
- below for each providers, where *<JWT>* stands for the provider's name in upper-case:
49
- - *<APP_PREFIX>_<JWT>_BODY_DATA* (optional)
50
- - *<APP_PREFIX>_<JWT>_CUSTOM_AUTH* (optional)
51
- - *<APP_PREFIX>_<JWT>_HEADER_DATA* (optional)
52
- - *<APP_PREFIX>_<JWT>_USER_ID* (required)
53
- - *<APP_PREFIX>_<JWT>_USER_SECRET* (required)
54
- - *<APP_PREFIX>_<JWT>_URL_TOKEN* (required)
55
-
56
- 2. The special environment variable *<APP_PREFIX>_IAM_PROVIDER_ENDPOINT* identifies the endpoint from which
57
- to obtain JWT tokens. It is not part of the *JWT* providers' setup, but is meant to be used
58
- by function *provider_setup_endpoint()*, wherein the value in that variable would represent the
59
- default value for its parameter.
60
-
61
- :return: the configuration data for the select *JWT* providers.
52
+ 1. Specify *<APP_PREFIX>_AUTH_PROVIDERS* with a list of names (typically, in lower-case), and the data set
53
+ below for each providers, where *<IAM>* stands for the provider's name in upper-case:
54
+ - *<APP_PREFIX>_<IAM>_BODY_DATA* (optional)
55
+ - *<APP_PREFIX>_<IAM>_CUSTOM_AUTH* (optional)
56
+ - *<APP_PREFIX>_<IAM>_HEADER_DATA* (optional)
57
+ - *<APP_PREFIX>_<IAM>_USER_ID* (required)
58
+ - *<APP_PREFIX>_<IAM>_USER_SECRET* (required)
59
+ - *<APP_PREFIX>_<IAM>_URL_TOKEN* (required)
60
+
61
+ 2. The special environment variable *<APP_PREFIX>_PROVIDER_ENDPOINT_TOKEN* identifies the endpoint
62
+ from which to obtain JWT tokens. It is not part of the *JWT* providers' setup, but is meant to be
63
+ used by function *provider_setup_endpoint()*, wherein the value in that variable would represent
64
+ the default value for its parameter.
65
+
66
+ :return: the configuration data for the select *IAM* providers.
62
67
  """
63
68
  # initialize the return variable
64
69
  result: dict[str, dict[ProviderParam, Any]] = {}
65
70
 
66
- servers: list[str] = env_get_strs(key=f"{APP_PREFIX}_IAM_PROVIDERS") or []
67
- for server in servers:
68
- prefix = server.upper()
69
- result[server] = {
71
+ for provider in IamProvider:
72
+ prefix = provider.name
73
+ result[provider] = {
74
+ ProviderParam.USER_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_USER_ID"),
75
+ ProviderParam.USER_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_USER_SECRET"),
70
76
  ProviderParam.BODY_DATA: env_get_obj(key=f"{APP_PREFIX}_{prefix}_BODY_DATA"),
71
77
  ProviderParam.CUSTOM_AUTH: env_get_strs(key=f"{APP_PREFIX}_{prefix}_CUSTOM_AUTH"),
72
78
  ProviderParam.HEADER_DATA: env_get_obj(key=f"{APP_PREFIX}_{prefix}_HEADER_DATA"),
73
- ProviderParam.USER_ID: env_get_str(key=f"{APP_PREFIX}_{prefix}_USER_ID"),
74
- ProviderParam.USER_SECRET: env_get_str(key=f"{APP_PREFIX}_{prefix}_USER_SECRET"),
75
- ProviderParam.URL_AUTH: env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_AUTH"),
79
+ ProviderParam.URL_TOKEN: env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_TOKEN"),
76
80
  ProviderParam.ACCESS_TOKEN: None,
77
81
  ProviderParam.ACCESS_EXPIRATION: 0,
78
82
  ProviderParam.REFRESH_TOKEN: None,
@@ -85,19 +89,20 @@ def __get_provider_data() -> dict[str, dict[ProviderParam, Any]]:
85
89
  # structure:
86
90
  # {
87
91
  # <provider-id>: {
88
- # "url": <strl>,
89
- # "user": <str>,
90
- # "pwd": <str>,
92
+ # "body-data": <dict[str, str],
91
93
  # "custom-auth": <tuple[str, str]>,
92
94
  # "headers-data": <dict[str, str]>,
93
- # "body-data": <dict[str, str],
95
+ # "user-id": <str>,
96
+ # "user-secret": <str>,
97
+ # "url-token": <strl>,
98
+ # # dinamically set
94
99
  # "access-token": <str>,
95
100
  # "access-expiration": <timestamp>,
96
101
  # "refresh-token": <str>,
97
102
  # "refresh-expiration": <timestamp>
98
103
  # }
99
104
  # }
100
- _provider_registry: Final[dict[str, dict[str, Any]]] = __get_provider_data()
105
+ _provider_registry: Final[dict[IamProvider, dict[str, Any]]] = __get_provider_data()
101
106
 
102
107
  # the lock protecting the data in '_provider_registry'
103
108
  # (because it is 'Final' and set at declaration time, it can be accessed through simple imports)
@@ -105,15 +110,15 @@ _provider_lock: Final[Lock] = Lock()
105
110
 
106
111
 
107
112
  @func_capture_params
108
- def provider_setup_server(provider_id: str,
109
- user_id: str = None,
110
- user_secret: str = None,
111
- custom_auth: tuple[str, str] = None,
112
- header_data: dict[str, str] = None,
113
- body_data: dict[str, str] = None,
114
- url_auth: str = None) -> None:
113
+ def iam_setup_provider(iam_provider: IamProvider,
114
+ user_id: str = None,
115
+ user_secret: str = None,
116
+ custom_auth: tuple[str, str] = None,
117
+ header_data: dict[str, str] = None,
118
+ body_data: dict[str, str] = None,
119
+ url_token: str = None) -> None:
115
120
  """
116
- Setup the *JWT* provider *provider_id*.
121
+ Setup the *IAM* provider *iam_provider*.
117
122
 
118
123
  For the parameters not effectively passed, an attempt is made to obtain a value from the corresponding
119
124
  environment variable.
@@ -122,17 +127,18 @@ def provider_setup_server(provider_id: str,
122
127
  as key-value pairs in the body of the request. Otherwise, the external provider *provider_id* uses the standard
123
128
  HTTP Basic Authorization scheme, wherein the credentials are B64-encoded and sent in the request headers.
124
129
 
125
- Optional constant key-value pairs (such as ['Content-Type', 'application/x-www-form-urlencoded']), to be
126
- added to the request headers, may be specified in *headers_data*. Likewise, optional constant key-value pairs
127
- (such as ['grant_type', 'client_credentials']), to be added to the request body, may be specified in *body_data*.
130
+ Optional constant key-value pairs (such as *['Content-Type', 'application/x-www-form-urlencoded']*),
131
+ to be added to the request headers, may be specified in *headers_data*. Likewise, optional constant
132
+ key-value pairs (such as *['grant_type', 'client_credentials']*), to be added to the request body,
133
+ may be specified in *body_data*.
128
134
 
129
- :param provider_id: the provider's identification
135
+ :param iam_provider: the provider's identification
130
136
  :param user_id: the basic authorization user
131
137
  :param user_secret: the basic authorization password
132
138
  :param custom_auth: optional key names for sending the credentials as key-value pairs in the body of the request
133
139
  :param header_data: optional key-value pairs to be added to the request headers
134
140
  :param body_data: optional key-value pairs to be added to the request body
135
- :param url_auth: the url to request *JWT* tokens with
141
+ :param url_token: the url to request *JWT* tokens with
136
142
  """
137
143
  global _provider_registry
138
144
 
@@ -140,7 +146,7 @@ def provider_setup_server(provider_id: str,
140
146
  defaulted_params: list[str] = func_defaulted_params.get()
141
147
 
142
148
  # read from the environment variables
143
- prefix: str = provider_id.upper()
149
+ prefix: str = iam_provider.name
144
150
  if "user_id" in defaulted_params:
145
151
  user_id = env_get_str(key=f"{APP_PREFIX}_{prefix}_USER_ID")
146
152
  if "user_secret" in defaulted_params:
@@ -151,17 +157,17 @@ def provider_setup_server(provider_id: str,
151
157
  header_data = env_get_obj(key=f"{APP_PREFIX}_{prefix}_HEADER_DATA")
152
158
  if "body_data" in defaulted_params:
153
159
  body_data = env_get_obj(key=f"{APP_PREFIX}_{prefix}_BODY_DATA")
154
- if "url_auth" in defaulted_params:
155
- url_auth = env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_AUTH")
160
+ if "url_token" in defaulted_params:
161
+ url_token = env_get_str(key=f"{APP_PREFIX}_{prefix}_URL_TOKEN")
156
162
 
157
163
  with _provider_lock:
158
- _provider_registry[provider_id] = {
159
- ProviderParam.URL_AUTH: url_auth,
160
- ProviderParam.USER_ID: user_id,
161
- ProviderParam.USER_SECRET: user_secret,
164
+ _provider_registry[iam_provider] = {
165
+ ProviderParam.BODY_DATA: body_data,
162
166
  ProviderParam.CUSTOM_AUTH: custom_auth,
163
167
  ProviderParam.HEADER_DATA: header_data,
164
- ProviderParam.BODY_DATA: body_data,
168
+ ProviderParam.USER_ID: user_id,
169
+ ProviderParam.USER_SECRET: user_secret,
170
+ ProviderParam.URL_TOKEN: url_token,
165
171
  # dynamically set
166
172
  ProviderParam.ACCESS_TOKEN: None,
167
173
  ProviderParam.ACCESS_EXPIRATION: 0,
@@ -187,12 +193,12 @@ def provider_setup_endpoint(flask_app: Flask,
187
193
 
188
194
  # read from the environment variable
189
195
  if "provider_endpoint" in defaulted_params:
190
- provider_endpoint = env_get_str(key=f"{APP_PREFIX}_IAM_PROVIDER_ENDPOINT")
196
+ provider_endpoint = env_get_str(key=f"{APP_PREFIX}_PROVIDER_ENDPOINT_TOKEN")
191
197
 
192
198
  # establish the endpoints
193
199
  if provider_endpoint:
194
200
  flask_app.add_url_rule(rule=provider_endpoint,
195
- endpoint=f"jwt-callback",
201
+ endpoint=f"provider-get-token",
196
202
  view_func=service_get_token,
197
203
  methods=["GET"])
198
204
 
@@ -207,7 +213,7 @@ def provider_setup_logger(logger: Logger) -> None:
207
213
  __JWT_LOGGER = logger
208
214
 
209
215
 
210
- # @flask_app.route(rule=<token_endpoint>, # IAM_PROVIDER_ENDPOINT_TOKEN
216
+ # @flask_app.route(rule=<token_endpoint>,
211
217
  # methods=["GET"])
212
218
  def service_get_token() -> Response:
213
219
  """
@@ -222,24 +228,27 @@ def service_get_token() -> Response:
222
228
 
223
229
  :return: *Response* containing the JWT token, or *BAD REQUEST*
224
230
  """
231
+ # retrieve the request arguments
232
+ args: dict[str, Any] = dict(request.args) or {}
233
+
225
234
  # log the request
226
235
  if __JWT_LOGGER:
227
- params: str = json.dumps(obj=request.args,
228
- ensure_ascii=False)
229
- __JWT_LOGGER.debug(msg=f"Request {request.method}:{request.path}, params {params}")
236
+ __JWT_LOGGER.debug(msg=f"Request {request.method}:{request.path}; {json.dumps(obj=args,
237
+ ensure_ascii=False)}")
230
238
 
231
239
  # obtain the provider JWT
232
- provider_id: str = request.args.get("jwt-provider")
240
+ provider_id: str = args.get("iam-provider")
241
+ iam_provider: IamProvider = IamProvider(provider_id) if provider_id in IamProvider else None
233
242
 
234
243
  # retrieve the token
235
244
  token: str | None = None
236
245
  errors: list[str] = []
237
- if provider_id:
238
- token: str = provider_get_token(provider_id=provider_id,
246
+ if iam_provider:
247
+ token: str = provider_get_token(iam_provider=iam_provider,
239
248
  errors=errors,
240
249
  logger=__JWT_LOGGER)
241
250
  else:
242
- msg: str = "JWT provider not informed"
251
+ msg: str = "IAM provider unknown or not informed"
243
252
  errors.append(msg)
244
253
  if __JWT_LOGGER:
245
254
  __JWT_LOGGER.error(msg=msg)
@@ -257,13 +266,13 @@ def service_get_token() -> Response:
257
266
  return result
258
267
 
259
268
 
260
- def provider_get_token(provider_id: str,
269
+ def provider_get_token(iam_provider: IamProvider,
261
270
  errors: list[str] = None,
262
271
  logger: Logger = None) -> str | None:
263
272
  """
264
273
  Obtain an JWT token from the external provider *provider_id*.
265
274
 
266
- :param provider_id: the provider's identification
275
+ :param iam_provider: the provider's identification
267
276
  :param errors: incidental error messages
268
277
  :param logger: optional logger
269
278
  :return: the JWT token, or *None* if error
@@ -274,7 +283,7 @@ def provider_get_token(provider_id: str,
274
283
  result: str | None = None
275
284
 
276
285
  with _provider_lock:
277
- provider: dict[str, Any] = _provider_registry.get(provider_id)
286
+ provider: dict[str, Any] = _provider_registry.get(iam_provider)
278
287
  if provider:
279
288
  now: int = int(datetime.now(tz=TZ_LOCAL).timestamp())
280
289
  if now < provider.get(ProviderParam.ACCESS_EXPIRATION):
@@ -284,7 +293,7 @@ def provider_get_token(provider_id: str,
284
293
  # access token has expired
285
294
  header_data: dict[str, str] | None = None
286
295
  body_data: dict[str, str] | None = None
287
- url: str = provider.get(ProviderParam.URL_AUTH)
296
+ url: str = provider.get(ProviderParam.URL_TOKEN)
288
297
  refresh_token: str = provider.get(ProviderParam.REFRESH_TOKEN)
289
298
  if refresh_token:
290
299
  # refresh token exists
@@ -298,7 +307,7 @@ def provider_get_token(provider_id: str,
298
307
  "grant_type": "refresh_token",
299
308
  "refresh_token": refresh_token
300
309
  }
301
- if not body_data:
310
+ if not header_data:
302
311
  # refresh token does not exist or has expired
303
312
  user: str = provider.get(ProviderParam.USER_ID)
304
313
  pwd: str = provider.get(ProviderParam.USER_SECRET)
@@ -330,7 +339,7 @@ def provider_get_token(provider_id: str,
330
339
  if refresh_exp else sys.maxsize
331
340
 
332
341
  elif logger or isinstance(errors, list):
333
- msg: str = f"Unknown provider '{provider_id}'"
342
+ msg: str = f"Unknown provider '{iam_provider}'"
334
343
  if logger:
335
344
  logger.error(msg=msg)
336
345
  if isinstance(errors, list):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pypomes_iam
3
- Version: 0.8.0
3
+ Version: 0.8.9
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=TU9WVKe9CdfEH2bFnLv249Hzo9xIq6NT8SgIUzWYlZE,1685
2
+ pypomes_iam/iam_actions.py,sha256=NY3gsZK72JQAUAwt8LsaSYhrbXdxyIcbzaG1U-VNF70,51234
3
+ pypomes_iam/iam_common.py,sha256=P2IzDQhfrf40VZBkC4p4pOUbR0IW7kq5_xmcAzPvW5s,17546
4
+ pypomes_iam/iam_pomes.py,sha256=2yLTOY_A8bkM7QsOynJOdmVoMu8ZSwFaO7RmxHVDDsU,8931
5
+ pypomes_iam/iam_services.py,sha256=4k2MEKQG5xwHuZylVLFGyohXRlraB4BXBOVPWxig2tg,26689
6
+ pypomes_iam/provider_pomes.py,sha256=uhH8tqdA6AVC8TiPfFdH5gkrrEw_cpkLerRFICdks58,17954
7
+ pypomes_iam/token_pomes.py,sha256=KiTlBNj3HURbZS_Rmti2RC6hny8VFPpbXeIO--HZ-fI,7703
8
+ pypomes_iam-0.8.9.dist-info/METADATA,sha256=y586FcTfUOqMNv9YRjmm_ssEbPVmNxxuQMrTrdDA5Kk,661
9
+ pypomes_iam-0.8.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ pypomes_iam-0.8.9.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
+ pypomes_iam-0.8.9.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- pypomes_iam/__init__.py,sha256=CbAH4lmonMxJTa3mWsWqA-pztmMb5-4Vsb4yA7HJ-hw,1561
2
- pypomes_iam/iam_actions.py,sha256=Le0uzKU9OfI9a5U0q2FvGDGrVwskMuV_e_kb5ZOveFQ,46227
3
- pypomes_iam/iam_common.py,sha256=xT1OFxX8wdRUN7GURL2kr6yBsh5j_Zb3iyqKykWEMYA,17523
4
- pypomes_iam/iam_pomes.py,sha256=IUwxMevYvDsH-PVNkbOF-b0U_W_5ACi-REBjm1ZT2PE,8421
5
- pypomes_iam/iam_services.py,sha256=bWPxzIsZFD9fP7bkLKqgi7Z23h107Qd_GKgL2KjQzuE,22995
6
- pypomes_iam/provider_pomes.py,sha256=gkE0LvOzfPASDCilPRhqSql4LhU6sCilucE5zbM3lF8,17534
7
- pypomes_iam/token_pomes.py,sha256=KiTlBNj3HURbZS_Rmti2RC6hny8VFPpbXeIO--HZ-fI,7703
8
- pypomes_iam-0.8.0.dist-info/METADATA,sha256=olIXbMd6htlsfw0TuEd4aSYEFwNuL_dne4tphUxwILM,661
9
- pypomes_iam-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- pypomes_iam-0.8.0.dist-info/licenses/LICENSE,sha256=YvUELgV8qvXlaYsy9hXG5EW3Bmsrkw-OJmmILZnonAc,1086
11
- pypomes_iam-0.8.0.dist-info/RECORD,,