pypomes-iam 0.7.6__py3-none-any.whl → 0.8.0__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.
- pypomes_iam/__init__.py +22 -12
- pypomes_iam/iam_actions.py +67 -50
- pypomes_iam/iam_common.py +70 -29
- pypomes_iam/iam_pomes.py +111 -98
- pypomes_iam/iam_services.py +227 -96
- pypomes_iam/provider_pomes.py +197 -30
- pypomes_iam/token_pomes.py +27 -0
- {pypomes_iam-0.7.6.dist-info → pypomes_iam-0.8.0.dist-info}/METADATA +2 -2
- pypomes_iam-0.8.0.dist-info/RECORD +11 -0
- pypomes_iam-0.7.6.dist-info/RECORD +0 -11
- {pypomes_iam-0.7.6.dist-info → pypomes_iam-0.8.0.dist-info}/WHEEL +0 -0
- {pypomes_iam-0.7.6.dist-info → pypomes_iam-0.8.0.dist-info}/licenses/LICENSE +0 -0
pypomes_iam/iam_services.py
CHANGED
|
@@ -9,8 +9,8 @@ from .iam_common import (
|
|
|
9
9
|
_iam_server_from_endpoint, _iam_server_from_issuer
|
|
10
10
|
)
|
|
11
11
|
from .iam_actions import (
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
iam_login, iam_logout,
|
|
13
|
+
iam_get_token, iam_exchange, iam_callback
|
|
14
14
|
)
|
|
15
15
|
from .token_pomes import token_get_claims, token_validate
|
|
16
16
|
|
|
@@ -23,7 +23,10 @@ def jwt_required(func: callable) -> callable:
|
|
|
23
23
|
"""
|
|
24
24
|
Create a decorator to authenticate service endpoints with JWT tokens.
|
|
25
25
|
|
|
26
|
+
The decorated function must be a registered endpoint to a *Flask* application.
|
|
27
|
+
|
|
26
28
|
:param func: the function being decorated
|
|
29
|
+
:return: the return from the call to *func*, or a *Response NOT AUTHORIZED* if the authentication failed
|
|
27
30
|
"""
|
|
28
31
|
# ruff: noqa: ANN003 - Missing type annotation for *{name}
|
|
29
32
|
def wrapper(*args, **kwargs) -> Response:
|
|
@@ -60,6 +63,7 @@ def __request_validate(request: Request) -> Response:
|
|
|
60
63
|
claims: dict[str, Any] = token_get_claims(token=token)
|
|
61
64
|
if claims:
|
|
62
65
|
issuer: str = claims["payload"].get("iss")
|
|
66
|
+
public_key: str | None = None
|
|
63
67
|
recipient_attr: str | None = None
|
|
64
68
|
recipient_id: str = request.values.get("user-id") or request.values.get("login")
|
|
65
69
|
with _iam_lock:
|
|
@@ -74,17 +78,17 @@ def __request_validate(request: Request) -> Response:
|
|
|
74
78
|
logger=__IAM_LOGGER)
|
|
75
79
|
if registry:
|
|
76
80
|
recipient_attr = registry[IamParam.RECIPIENT_ATTR]
|
|
77
|
-
public_key
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
public_key = _get_public_key(iam_server=iam_server,
|
|
82
|
+
errors=None,
|
|
83
|
+
logger=__IAM_LOGGER)
|
|
80
84
|
# validate the token (log errors, only)
|
|
81
85
|
errors: list[str] = []
|
|
82
|
-
if
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
if token_validate(token=token,
|
|
87
|
+
issuer=issuer,
|
|
88
|
+
recipient_id=recipient_id,
|
|
89
|
+
recipient_attr=recipient_attr,
|
|
90
|
+
public_key=public_key,
|
|
91
|
+
errors=errors):
|
|
88
92
|
# token is valid
|
|
89
93
|
bad_token = False
|
|
90
94
|
elif __IAM_LOGGER:
|
|
@@ -99,7 +103,7 @@ def __request_validate(request: Request) -> Response:
|
|
|
99
103
|
return result
|
|
100
104
|
|
|
101
105
|
|
|
102
|
-
def
|
|
106
|
+
def iam_setup_logger(logger: Logger) -> None:
|
|
103
107
|
"""
|
|
104
108
|
Register the logger for HTTP services.
|
|
105
109
|
|
|
@@ -109,13 +113,66 @@ def logger_register(logger: Logger) -> None:
|
|
|
109
113
|
__IAM_LOGGER = logger
|
|
110
114
|
|
|
111
115
|
|
|
112
|
-
# @flask_app.route(rule=<
|
|
113
|
-
# methods=["
|
|
114
|
-
|
|
116
|
+
# @flask_app.route(rule=<setup_server_endpoint>,
|
|
117
|
+
# methods=["POST"])
|
|
118
|
+
def service_setup_server() -> Response:
|
|
119
|
+
"""
|
|
120
|
+
Entry point to setup a *IAM* server.
|
|
121
|
+
|
|
122
|
+
These are the expected parameters in the request's body, in a JSON or as form data:
|
|
123
|
+
- *iam_server*: identifies the supported *IAM* server (currently, *jusbr* or *keycloak*)
|
|
124
|
+
- *admin_id*: identifies the realm administrator
|
|
125
|
+
- *admin_secret*: password for the realm administrator
|
|
126
|
+
- *client_id*: the client's identification with the *IAM* server
|
|
127
|
+
- *client_realm*: the client's realm
|
|
128
|
+
- *client_secret*: the client's password with the *IAM* server
|
|
129
|
+
- *login_timeout*: timeout for login authentication (in seconds,defaults to no timeout)
|
|
130
|
+
- *k_lifetime*: how long to use *IAM* server's public key, before refreshing it (in seconds)
|
|
131
|
+
- *recipient_attr*: attribute in the token's payload holding the token's subject
|
|
132
|
+
- *rl_base*: base URL to request services
|
|
133
|
+
|
|
134
|
+
For the parameters not effectively passed, an attempt is made to obtain a value from the corresponding
|
|
135
|
+
environment variables. Most parameters are required to have values, which must be assigned either
|
|
136
|
+
throught the function invocation, or from the corresponding environment variables.
|
|
137
|
+
|
|
138
|
+
The parameters *admin_id* and *admin_secret* are required only if performing administrative tasks is intended.
|
|
139
|
+
The optional parameter *ogin_timeout* refers to the maximum time in seconds allowed for the user
|
|
140
|
+
to login at the *IAM* server's login page, and defaults to no time limit.
|
|
141
|
+
|
|
142
|
+
The parameter *client_secret* is required in most requests to the *IAM* server. In the case
|
|
143
|
+
it is not provided, but *admin_id* and *admin_secret* are, it is obtained from the *IAM* server itself
|
|
144
|
+
the first time it is needed.
|
|
145
|
+
|
|
146
|
+
:return: *Response OK*
|
|
147
|
+
"""
|
|
148
|
+
# log the request
|
|
149
|
+
if __IAM_LOGGER:
|
|
150
|
+
__IAM_LOGGER.debug(msg=f"{_log_init(request=request)}; {json.dumps(obj=request.args,
|
|
151
|
+
ensure_ascii=False)}")
|
|
152
|
+
# retrieve the arguments
|
|
153
|
+
args: dict[str, Any] = request.json if request.is_json else request.form
|
|
154
|
+
|
|
155
|
+
# setup the server
|
|
156
|
+
from .iam_pomes import iam_setup_server
|
|
157
|
+
iam_setup_server(**args)
|
|
158
|
+
result = Response(status=200)
|
|
159
|
+
|
|
160
|
+
# log the response
|
|
161
|
+
if __IAM_LOGGER:
|
|
162
|
+
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
163
|
+
|
|
164
|
+
return result
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# @flask_app.route(rule=<login_endpoint>, # IAM_ENDPOINT_LOGIN
|
|
115
168
|
# methods=["GET"])
|
|
116
169
|
def service_login() -> Response:
|
|
117
170
|
"""
|
|
118
|
-
Entry point for the IAM server's login service.
|
|
171
|
+
Entry point for the *IAM* server's login service.
|
|
172
|
+
|
|
173
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
174
|
+
the name of the *IAM* server in charge of handling this service. This prefixing is done automatically
|
|
175
|
+
if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
119
176
|
|
|
120
177
|
These are the expected request parameters:
|
|
121
178
|
- user-id: optional, identifies the reference user (alias: 'login')
|
|
@@ -146,10 +203,10 @@ def service_login() -> Response:
|
|
|
146
203
|
logger=__IAM_LOGGER)
|
|
147
204
|
if iam_server:
|
|
148
205
|
# obtain the login URL
|
|
149
|
-
login_url: str =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
206
|
+
login_url: str = iam_login(iam_server=iam_server,
|
|
207
|
+
args=request.args,
|
|
208
|
+
errors=errors,
|
|
209
|
+
logger=__IAM_LOGGER)
|
|
153
210
|
if login_url:
|
|
154
211
|
result = jsonify({"login-url": login_url})
|
|
155
212
|
if errors:
|
|
@@ -158,18 +215,20 @@ def service_login() -> Response:
|
|
|
158
215
|
|
|
159
216
|
# log the response
|
|
160
217
|
if __IAM_LOGGER:
|
|
161
|
-
__IAM_LOGGER.debug(msg=f"Response {result}
|
|
218
|
+
__IAM_LOGGER.debug(msg=f"Response {result}; {result.get_data(as_text=True)}")
|
|
162
219
|
|
|
163
220
|
return result
|
|
164
221
|
|
|
165
222
|
|
|
166
|
-
# @flask_app.route(rule=<logout_endpoint>, #
|
|
167
|
-
# methods=["GET"])
|
|
168
|
-
# @flask_app.route(rule=<login_endpoint>, # KEYCLOAK_ENDPOINT_LOGOUT
|
|
223
|
+
# @flask_app.route(rule=<logout_endpoint>, # IAM_ENDPOINT_LOGOUT
|
|
169
224
|
# methods=["GET"])
|
|
170
225
|
def service_logout() -> Response:
|
|
171
226
|
"""
|
|
172
|
-
Entry point for the IAM server's logout service.
|
|
227
|
+
Entry point for the *IAM* server's logout service.
|
|
228
|
+
|
|
229
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
230
|
+
the name of the *IAM* server in charge of handling this service. This prefixing is done automatically
|
|
231
|
+
if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
173
232
|
|
|
174
233
|
The user is identified by the attribute *user-id* or "login", provided as a request parameter.
|
|
175
234
|
If successful, remove all data relating to the user from the *IAM* server's registry.
|
|
@@ -192,30 +251,32 @@ def service_logout() -> Response:
|
|
|
192
251
|
logger=__IAM_LOGGER)
|
|
193
252
|
if iam_server:
|
|
194
253
|
# logout the user
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
254
|
+
iam_logout(iam_server=iam_server,
|
|
255
|
+
args=request.args,
|
|
256
|
+
errors=errors,
|
|
257
|
+
logger=__IAM_LOGGER)
|
|
199
258
|
if errors:
|
|
200
259
|
result = Response(response="; ".join(errors),
|
|
201
260
|
status=400)
|
|
202
261
|
else:
|
|
203
262
|
result = Response(status=204)
|
|
204
263
|
|
|
205
|
-
# log the response
|
|
206
264
|
if __IAM_LOGGER:
|
|
265
|
+
# log the response
|
|
207
266
|
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
208
267
|
|
|
209
268
|
return result
|
|
210
269
|
|
|
211
270
|
|
|
212
|
-
# @flask_app.route(rule=<callback_endpoint>, #
|
|
271
|
+
# @flask_app.route(rule=<callback_endpoint>, # IAM_ENDPOINT_CALLBACK
|
|
213
272
|
# methods=["GET", "POST"])
|
|
214
|
-
# @flask_app.route(rule=<callback_endpoint>, # KEYCLOAK_ENDPOINT_CALLBACK
|
|
215
|
-
# methods=["POST"])
|
|
216
273
|
def service_callback() -> Response:
|
|
217
274
|
"""
|
|
218
|
-
Entry point for the callback from the IAM server on authentication operation.
|
|
275
|
+
Entry point for the callback from the *IAM* server on authentication operation.
|
|
276
|
+
|
|
277
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
278
|
+
the name of the *IAM* server in charge of handling this service. This prefixing is done automatically
|
|
279
|
+
if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
219
280
|
|
|
220
281
|
This callback is invoked from a front-end application after a successful login at the
|
|
221
282
|
*IAM* server's login page, forwarding the data received. In a typical OAuth2 flow faction,
|
|
@@ -246,10 +307,10 @@ def service_callback() -> Response:
|
|
|
246
307
|
logger=__IAM_LOGGER)
|
|
247
308
|
if iam_server:
|
|
248
309
|
# process the callback operation
|
|
249
|
-
token_data =
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
310
|
+
token_data = iam_callback(iam_server=iam_server,
|
|
311
|
+
args=request.args,
|
|
312
|
+
errors=errors,
|
|
313
|
+
logger=__IAM_LOGGER)
|
|
253
314
|
result: Response
|
|
254
315
|
if errors:
|
|
255
316
|
result = jsonify({"errors": "; ".join(errors)})
|
|
@@ -264,60 +325,131 @@ def service_callback() -> Response:
|
|
|
264
325
|
return result
|
|
265
326
|
|
|
266
327
|
|
|
267
|
-
# @flask_app.route(rule=<
|
|
268
|
-
# methods=["
|
|
269
|
-
|
|
270
|
-
# methods=["GET"])
|
|
271
|
-
def service_token() -> Response:
|
|
328
|
+
# @flask_app.route(rule=<callback_endpoint>, # KEYCLOAK_ENDPOINT_EXCHANGE
|
|
329
|
+
# methods=["POST"])
|
|
330
|
+
def service_exchange() -> Response:
|
|
272
331
|
"""
|
|
273
|
-
Entry point for
|
|
332
|
+
Entry point for requesting the *IAM* server to exchange the token.
|
|
274
333
|
|
|
275
|
-
|
|
334
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
335
|
+
the name of the *IAM* server in charge of handling this service. This prefixing is done automatically
|
|
336
|
+
if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
337
|
+
|
|
338
|
+
If the exchange is successful, the token data is stored in the *IAM* server's registry, and returned.
|
|
339
|
+
Otherwise, *errors* will contain the appropriate error message.
|
|
340
|
+
|
|
341
|
+
The expected request parameters are:
|
|
342
|
+
- user-id: identification for the reference user (alias: 'login')
|
|
343
|
+
- access-token: the token to be exchanged
|
|
276
344
|
|
|
277
345
|
On success, the returned *Response* will contain the following JSON:
|
|
278
346
|
{
|
|
279
347
|
"user-id": <reference-user-identification>,
|
|
280
|
-
"access-token": <token>
|
|
348
|
+
"access-token": <the-exchanged-token>
|
|
281
349
|
}
|
|
282
350
|
|
|
283
|
-
:return: *Response* containing the user
|
|
351
|
+
:return: *Response* containing the reference user identification and the token, or *BAD REQUEST*
|
|
284
352
|
"""
|
|
285
353
|
# log the request
|
|
286
354
|
if __IAM_LOGGER:
|
|
287
355
|
__IAM_LOGGER.debug(msg=_log_init(request=request))
|
|
288
356
|
|
|
289
|
-
# obtain the user's identification
|
|
290
|
-
args: dict[str, Any] = request.args
|
|
291
|
-
user_id: str = args.get("user-id") or args.get("login")
|
|
292
|
-
|
|
293
357
|
errors: list[str] = []
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
logger=__IAM_LOGGER)
|
|
308
|
-
else:
|
|
309
|
-
msg: str = "User identification not provided"
|
|
310
|
-
errors.append(msg)
|
|
311
|
-
if __IAM_LOGGER:
|
|
312
|
-
__IAM_LOGGER.error(msg=msg)
|
|
313
|
-
|
|
358
|
+
with _iam_lock:
|
|
359
|
+
# retrieve the IAM server
|
|
360
|
+
iam_server: IamServer = _iam_server_from_endpoint(endpoint=request.endpoint,
|
|
361
|
+
errors=errors,
|
|
362
|
+
logger=__IAM_LOGGER)
|
|
363
|
+
# exchange the token
|
|
364
|
+
token_info: tuple[str, str] | None = None
|
|
365
|
+
if iam_server:
|
|
366
|
+
errors: list[str] = []
|
|
367
|
+
token_info = iam_exchange(iam_server=iam_server,
|
|
368
|
+
args=request.args,
|
|
369
|
+
errors=errors,
|
|
370
|
+
logger=__IAM_LOGGER)
|
|
314
371
|
result: Response
|
|
315
372
|
if errors:
|
|
316
373
|
result = Response(response="; ".join(errors),
|
|
317
374
|
status=400)
|
|
318
375
|
else:
|
|
319
|
-
result = jsonify({"user-id":
|
|
320
|
-
"access-token":
|
|
376
|
+
result = jsonify({"user-id": token_info[0],
|
|
377
|
+
"access-token": token_info[1]})
|
|
378
|
+
if __IAM_LOGGER:
|
|
379
|
+
# log the response (the returned data is not logged, as it contains the token)
|
|
380
|
+
__IAM_LOGGER.debug(msg=f"Response {result}; {result.get_data(as_text=True)}")
|
|
381
|
+
|
|
382
|
+
return result
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# @flask_app.route(rule=/iam/jusbr:callback-exchange,
|
|
386
|
+
# methods=["GET"])
|
|
387
|
+
def service_callback_exchange() -> Response:
|
|
388
|
+
"""
|
|
389
|
+
Entry point for the callback from the IAM server on authentication operation, with subsequent token exchange.
|
|
390
|
+
|
|
391
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
392
|
+
the name of the *IAM* server in charge of handling this service, and suffixed with the string *_to_*
|
|
393
|
+
followed by the name of the *IAM* server in charge of the token exchange. The prefixing, but not the suffixing,
|
|
394
|
+
is done automatically if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
395
|
+
|
|
396
|
+
This callback is invoked from a front-end application after a successful login at the
|
|
397
|
+
*IAM* server's login page, forwarding the data received. In a typical OAuth2 flow faction,
|
|
398
|
+
this data is then used to effectively obtain the token from the *IAM* server.
|
|
399
|
+
This token is stored and thereafter, a corresponding token is requested from another IAM *server*,
|
|
400
|
+
in a scheme known as "token exchange". This new token, along with the reference user identification,
|
|
401
|
+
are then stored. Note that the original token is the one actually returned.
|
|
402
|
+
|
|
403
|
+
The relevant expected request arguments are:
|
|
404
|
+
- *state*: used to enhance security during the authorization process, typically to provide *CSRF* protection
|
|
405
|
+
- *code*: the temporary authorization code provided by the IAM server, to be exchanged for the token
|
|
406
|
+
|
|
407
|
+
On success, the returned *Response* will contain the following JSON:
|
|
408
|
+
{
|
|
409
|
+
"user-id": <reference-user-identification>,
|
|
410
|
+
"access-token": <the-original-token>
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
:return: *Response* containing the reference user identification and the token, or *BAD REQUEST*
|
|
414
|
+
"""
|
|
415
|
+
# declare the return variable
|
|
416
|
+
result: Response | None = None
|
|
417
|
+
|
|
418
|
+
# log the request
|
|
419
|
+
if __IAM_LOGGER:
|
|
420
|
+
__IAM_LOGGER.debug(msg=_log_init(request=request))
|
|
421
|
+
|
|
422
|
+
errors: list[str] = []
|
|
423
|
+
with _iam_lock:
|
|
424
|
+
# retrieve the IAM server
|
|
425
|
+
iam_server: IamServer = _iam_server_from_endpoint(endpoint=request.endpoint,
|
|
426
|
+
errors=errors,
|
|
427
|
+
logger=__IAM_LOGGER)
|
|
428
|
+
# obtain the login URL
|
|
429
|
+
token_info: tuple[str, str] = iam_callback(iam_server=iam_server,
|
|
430
|
+
args=request.args,
|
|
431
|
+
errors=errors,
|
|
432
|
+
logger=__IAM_LOGGER)
|
|
433
|
+
if token_info:
|
|
434
|
+
args: dict[str, str] = {
|
|
435
|
+
"user-id": token_info[0],
|
|
436
|
+
"access-token": token_info[1]
|
|
437
|
+
}
|
|
438
|
+
# retrieve the exchange IAM server
|
|
439
|
+
pos: int = request.endpoint.index("_to_")
|
|
440
|
+
exchange_server: IamServer = _iam_server_from_endpoint(endpoint=request.endpoint[pos+4],
|
|
441
|
+
errors=errors,
|
|
442
|
+
logger=__IAM_LOGGER)
|
|
443
|
+
token_info = iam_exchange(iam_server=exchange_server,
|
|
444
|
+
args=args,
|
|
445
|
+
logger=__IAM_LOGGER)
|
|
446
|
+
if token_info:
|
|
447
|
+
result = jsonify({"user-id": token_info[0],
|
|
448
|
+
"access-token": token_info[1]})
|
|
449
|
+
if errors:
|
|
450
|
+
result = Response("; ".join(errors))
|
|
451
|
+
result.status_code = 400
|
|
452
|
+
|
|
321
453
|
if __IAM_LOGGER:
|
|
322
454
|
# log the response (the returned data is not logged, as it contains the token)
|
|
323
455
|
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
@@ -325,19 +457,17 @@ def service_token() -> Response:
|
|
|
325
457
|
return result
|
|
326
458
|
|
|
327
459
|
|
|
328
|
-
# @flask_app.route(rule=<
|
|
329
|
-
# methods=["
|
|
330
|
-
def
|
|
460
|
+
# @flask_app.route(rule=<token_endpoint>, # IAM_ENDPOINT_TOKEN
|
|
461
|
+
# methods=["GET"])
|
|
462
|
+
def service_get_token() -> Response:
|
|
331
463
|
"""
|
|
332
|
-
Entry point for
|
|
464
|
+
Entry point for retrieving a token from the *IAM* server.
|
|
333
465
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
- access-token: the token to be exchanged
|
|
466
|
+
When registering this endpoint, the name used in *Flask*'s *endpoint* parameter must be prefixed with
|
|
467
|
+
the name of the *IAM* server in charge of handling this service. This prefixing is done automatically
|
|
468
|
+
if the endpoint is established with a call to *iam_setup_endpoints()*.
|
|
338
469
|
|
|
339
|
-
|
|
340
|
-
Otherwise, *errors* will contain the appropriate error message.
|
|
470
|
+
The user is identified by the attribute *user-id* or "login", provided as a request parameter.
|
|
341
471
|
|
|
342
472
|
On success, the returned *Response* will contain the following JSON:
|
|
343
473
|
{
|
|
@@ -345,37 +475,38 @@ def service_exchange() -> Response:
|
|
|
345
475
|
"access-token": <token>
|
|
346
476
|
}
|
|
347
477
|
|
|
348
|
-
:return: *Response* containing the token
|
|
478
|
+
:return: *Response* containing the user reference identification and the token, or *BAD REQUEST*
|
|
349
479
|
"""
|
|
350
480
|
# log the request
|
|
351
481
|
if __IAM_LOGGER:
|
|
352
482
|
__IAM_LOGGER.debug(msg=_log_init(request=request))
|
|
353
483
|
|
|
484
|
+
# obtain the request arguments
|
|
485
|
+
args: dict[str, Any] = request.args
|
|
486
|
+
|
|
354
487
|
errors: list[str] = []
|
|
488
|
+
token_info: dict[str, str] | None = None
|
|
355
489
|
with _iam_lock:
|
|
356
|
-
# retrieve the IAM server
|
|
490
|
+
# retrieve the IAM server
|
|
357
491
|
iam_server: IamServer = _iam_server_from_endpoint(endpoint=request.endpoint,
|
|
358
492
|
errors=errors,
|
|
359
493
|
logger=__IAM_LOGGER)
|
|
360
|
-
# exchange the token
|
|
361
|
-
token_info: tuple[str, str] | None = None
|
|
362
494
|
if iam_server:
|
|
495
|
+
# retrieve the token
|
|
363
496
|
errors: list[str] = []
|
|
364
|
-
token_info =
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
497
|
+
token_info = iam_get_token(iam_server=iam_server,
|
|
498
|
+
args=args,
|
|
499
|
+
errors=errors,
|
|
500
|
+
logger=__IAM_LOGGER)
|
|
368
501
|
result: Response
|
|
369
502
|
if errors:
|
|
370
503
|
result = Response(response="; ".join(errors),
|
|
371
504
|
status=400)
|
|
372
505
|
else:
|
|
373
|
-
result = jsonify(
|
|
374
|
-
"access-token": token_info[1]})
|
|
375
|
-
|
|
376
|
-
# log the response
|
|
506
|
+
result = jsonify(token_info)
|
|
377
507
|
if __IAM_LOGGER:
|
|
378
|
-
|
|
508
|
+
# log the response (the returned data is not logged, as it contains the token)
|
|
509
|
+
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
379
510
|
|
|
380
511
|
return result
|
|
381
512
|
|