pypomes-iam 0.7.2__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 +116 -56
- pypomes_iam/iam_common.py +70 -29
- pypomes_iam/iam_pomes.py +111 -98
- pypomes_iam/iam_services.py +228 -96
- pypomes_iam/provider_pomes.py +197 -30
- pypomes_iam/token_pomes.py +27 -0
- {pypomes_iam-0.7.2.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.2.dist-info/RECORD +0 -11
- {pypomes_iam-0.7.2.dist-info → pypomes_iam-0.8.0.dist-info}/WHEEL +0 -0
- {pypomes_iam-0.7.2.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,17 +113,71 @@ 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')
|
|
122
179
|
- redirect-uri: a parameter to be added to the query part of the returned URL
|
|
180
|
+
-target-idp: optionally, identify a target identity provider for the login operation
|
|
123
181
|
|
|
124
182
|
If provided, the user identification will be validated against the authorization data
|
|
125
183
|
returned by *iam_server* upon login. On success, the following JSON, containing the appropriate
|
|
@@ -145,10 +203,10 @@ def service_login() -> Response:
|
|
|
145
203
|
logger=__IAM_LOGGER)
|
|
146
204
|
if iam_server:
|
|
147
205
|
# obtain the login URL
|
|
148
|
-
login_url: str =
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
206
|
+
login_url: str = iam_login(iam_server=iam_server,
|
|
207
|
+
args=request.args,
|
|
208
|
+
errors=errors,
|
|
209
|
+
logger=__IAM_LOGGER)
|
|
152
210
|
if login_url:
|
|
153
211
|
result = jsonify({"login-url": login_url})
|
|
154
212
|
if errors:
|
|
@@ -157,18 +215,20 @@ def service_login() -> Response:
|
|
|
157
215
|
|
|
158
216
|
# log the response
|
|
159
217
|
if __IAM_LOGGER:
|
|
160
|
-
__IAM_LOGGER.debug(msg=f"Response {result}
|
|
218
|
+
__IAM_LOGGER.debug(msg=f"Response {result}; {result.get_data(as_text=True)}")
|
|
161
219
|
|
|
162
220
|
return result
|
|
163
221
|
|
|
164
222
|
|
|
165
|
-
# @flask_app.route(rule=<logout_endpoint>, #
|
|
166
|
-
# methods=["GET"])
|
|
167
|
-
# @flask_app.route(rule=<login_endpoint>, # KEYCLOAK_ENDPOINT_LOGOUT
|
|
223
|
+
# @flask_app.route(rule=<logout_endpoint>, # IAM_ENDPOINT_LOGOUT
|
|
168
224
|
# methods=["GET"])
|
|
169
225
|
def service_logout() -> Response:
|
|
170
226
|
"""
|
|
171
|
-
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()*.
|
|
172
232
|
|
|
173
233
|
The user is identified by the attribute *user-id* or "login", provided as a request parameter.
|
|
174
234
|
If successful, remove all data relating to the user from the *IAM* server's registry.
|
|
@@ -191,30 +251,32 @@ def service_logout() -> Response:
|
|
|
191
251
|
logger=__IAM_LOGGER)
|
|
192
252
|
if iam_server:
|
|
193
253
|
# logout the user
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
254
|
+
iam_logout(iam_server=iam_server,
|
|
255
|
+
args=request.args,
|
|
256
|
+
errors=errors,
|
|
257
|
+
logger=__IAM_LOGGER)
|
|
198
258
|
if errors:
|
|
199
259
|
result = Response(response="; ".join(errors),
|
|
200
260
|
status=400)
|
|
201
261
|
else:
|
|
202
262
|
result = Response(status=204)
|
|
203
263
|
|
|
204
|
-
# log the response
|
|
205
264
|
if __IAM_LOGGER:
|
|
265
|
+
# log the response
|
|
206
266
|
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
207
267
|
|
|
208
268
|
return result
|
|
209
269
|
|
|
210
270
|
|
|
211
|
-
# @flask_app.route(rule=<callback_endpoint>, #
|
|
271
|
+
# @flask_app.route(rule=<callback_endpoint>, # IAM_ENDPOINT_CALLBACK
|
|
212
272
|
# methods=["GET", "POST"])
|
|
213
|
-
# @flask_app.route(rule=<callback_endpoint>, # KEYCLOAK_ENDPOINT_CALLBACK
|
|
214
|
-
# methods=["POST"])
|
|
215
273
|
def service_callback() -> Response:
|
|
216
274
|
"""
|
|
217
|
-
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()*.
|
|
218
280
|
|
|
219
281
|
This callback is invoked from a front-end application after a successful login at the
|
|
220
282
|
*IAM* server's login page, forwarding the data received. In a typical OAuth2 flow faction,
|
|
@@ -245,10 +307,10 @@ def service_callback() -> Response:
|
|
|
245
307
|
logger=__IAM_LOGGER)
|
|
246
308
|
if iam_server:
|
|
247
309
|
# process the callback operation
|
|
248
|
-
token_data =
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
310
|
+
token_data = iam_callback(iam_server=iam_server,
|
|
311
|
+
args=request.args,
|
|
312
|
+
errors=errors,
|
|
313
|
+
logger=__IAM_LOGGER)
|
|
252
314
|
result: Response
|
|
253
315
|
if errors:
|
|
254
316
|
result = jsonify({"errors": "; ".join(errors)})
|
|
@@ -263,60 +325,131 @@ def service_callback() -> Response:
|
|
|
263
325
|
return result
|
|
264
326
|
|
|
265
327
|
|
|
266
|
-
# @flask_app.route(rule=<
|
|
267
|
-
# methods=["
|
|
268
|
-
|
|
269
|
-
# methods=["GET"])
|
|
270
|
-
def service_token() -> Response:
|
|
328
|
+
# @flask_app.route(rule=<callback_endpoint>, # KEYCLOAK_ENDPOINT_EXCHANGE
|
|
329
|
+
# methods=["POST"])
|
|
330
|
+
def service_exchange() -> Response:
|
|
271
331
|
"""
|
|
272
|
-
Entry point for
|
|
332
|
+
Entry point for requesting the *IAM* server to exchange the token.
|
|
273
333
|
|
|
274
|
-
|
|
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
|
|
275
344
|
|
|
276
345
|
On success, the returned *Response* will contain the following JSON:
|
|
277
346
|
{
|
|
278
347
|
"user-id": <reference-user-identification>,
|
|
279
|
-
"access-token": <token>
|
|
348
|
+
"access-token": <the-exchanged-token>
|
|
280
349
|
}
|
|
281
350
|
|
|
282
|
-
:return: *Response* containing the user
|
|
351
|
+
:return: *Response* containing the reference user identification and the token, or *BAD REQUEST*
|
|
283
352
|
"""
|
|
284
353
|
# log the request
|
|
285
354
|
if __IAM_LOGGER:
|
|
286
355
|
__IAM_LOGGER.debug(msg=_log_init(request=request))
|
|
287
356
|
|
|
288
|
-
# obtain the user's identification
|
|
289
|
-
args: dict[str, Any] = request.args
|
|
290
|
-
user_id: str = args.get("user-id") or args.get("login")
|
|
291
|
-
|
|
292
357
|
errors: list[str] = []
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
logger=__IAM_LOGGER)
|
|
307
|
-
else:
|
|
308
|
-
msg: str = "User identification not provided"
|
|
309
|
-
errors.append(msg)
|
|
310
|
-
if __IAM_LOGGER:
|
|
311
|
-
__IAM_LOGGER.error(msg=msg)
|
|
312
|
-
|
|
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)
|
|
313
371
|
result: Response
|
|
314
372
|
if errors:
|
|
315
373
|
result = Response(response="; ".join(errors),
|
|
316
374
|
status=400)
|
|
317
375
|
else:
|
|
318
|
-
result = jsonify({"user-id":
|
|
319
|
-
"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
|
+
|
|
320
453
|
if __IAM_LOGGER:
|
|
321
454
|
# log the response (the returned data is not logged, as it contains the token)
|
|
322
455
|
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
@@ -324,19 +457,17 @@ def service_token() -> Response:
|
|
|
324
457
|
return result
|
|
325
458
|
|
|
326
459
|
|
|
327
|
-
# @flask_app.route(rule=<
|
|
328
|
-
# methods=["
|
|
329
|
-
def
|
|
460
|
+
# @flask_app.route(rule=<token_endpoint>, # IAM_ENDPOINT_TOKEN
|
|
461
|
+
# methods=["GET"])
|
|
462
|
+
def service_get_token() -> Response:
|
|
330
463
|
"""
|
|
331
|
-
Entry point for
|
|
464
|
+
Entry point for retrieving a token from the *IAM* server.
|
|
332
465
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
- 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()*.
|
|
337
469
|
|
|
338
|
-
|
|
339
|
-
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.
|
|
340
471
|
|
|
341
472
|
On success, the returned *Response* will contain the following JSON:
|
|
342
473
|
{
|
|
@@ -344,37 +475,38 @@ def service_exchange() -> Response:
|
|
|
344
475
|
"access-token": <token>
|
|
345
476
|
}
|
|
346
477
|
|
|
347
|
-
:return: *Response* containing the token
|
|
478
|
+
:return: *Response* containing the user reference identification and the token, or *BAD REQUEST*
|
|
348
479
|
"""
|
|
349
480
|
# log the request
|
|
350
481
|
if __IAM_LOGGER:
|
|
351
482
|
__IAM_LOGGER.debug(msg=_log_init(request=request))
|
|
352
483
|
|
|
484
|
+
# obtain the request arguments
|
|
485
|
+
args: dict[str, Any] = request.args
|
|
486
|
+
|
|
353
487
|
errors: list[str] = []
|
|
488
|
+
token_info: dict[str, str] | None = None
|
|
354
489
|
with _iam_lock:
|
|
355
|
-
# retrieve the IAM server
|
|
490
|
+
# retrieve the IAM server
|
|
356
491
|
iam_server: IamServer = _iam_server_from_endpoint(endpoint=request.endpoint,
|
|
357
492
|
errors=errors,
|
|
358
493
|
logger=__IAM_LOGGER)
|
|
359
|
-
# exchange the token
|
|
360
|
-
token_info: tuple[str, str] | None = None
|
|
361
494
|
if iam_server:
|
|
495
|
+
# retrieve the token
|
|
362
496
|
errors: list[str] = []
|
|
363
|
-
token_info =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
497
|
+
token_info = iam_get_token(iam_server=iam_server,
|
|
498
|
+
args=args,
|
|
499
|
+
errors=errors,
|
|
500
|
+
logger=__IAM_LOGGER)
|
|
367
501
|
result: Response
|
|
368
502
|
if errors:
|
|
369
503
|
result = Response(response="; ".join(errors),
|
|
370
504
|
status=400)
|
|
371
505
|
else:
|
|
372
|
-
result = jsonify(
|
|
373
|
-
"access-token": token_info[1]})
|
|
374
|
-
|
|
375
|
-
# log the response
|
|
506
|
+
result = jsonify(token_info)
|
|
376
507
|
if __IAM_LOGGER:
|
|
377
|
-
|
|
508
|
+
# log the response (the returned data is not logged, as it contains the token)
|
|
509
|
+
__IAM_LOGGER.debug(msg=f"Response {result}")
|
|
378
510
|
|
|
379
511
|
return result
|
|
380
512
|
|