pypomes-iam 0.2.9__py3-none-any.whl → 0.7.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.
Potentially problematic release.
This version of pypomes-iam might be problematic. Click here for more details.
- pypomes_iam/__init__.py +20 -8
- pypomes_iam/iam_actions.py +878 -0
- pypomes_iam/iam_common.py +295 -334
- pypomes_iam/iam_pomes.py +135 -192
- pypomes_iam/iam_services.py +394 -0
- pypomes_iam/provider_pomes.py +175 -72
- pypomes_iam/token_pomes.py +59 -7
- {pypomes_iam-0.2.9.dist-info → pypomes_iam-0.7.0.dist-info}/METADATA +1 -2
- pypomes_iam-0.7.0.dist-info/RECORD +11 -0
- pypomes_iam/jusbr_pomes.py +0 -114
- pypomes_iam/keycloak_pomes.py +0 -117
- pypomes_iam-0.2.9.dist-info/RECORD +0 -11
- {pypomes_iam-0.2.9.dist-info → pypomes_iam-0.7.0.dist-info}/WHEEL +0 -0
- {pypomes_iam-0.2.9.dist-info → pypomes_iam-0.7.0.dist-info}/licenses/LICENSE +0 -0
pypomes_iam/iam_pomes.py
CHANGED
|
@@ -1,213 +1,156 @@
|
|
|
1
|
-
import
|
|
2
|
-
import requests
|
|
3
|
-
from flask import Response, request, jsonify
|
|
1
|
+
from flask import Flask
|
|
4
2
|
from logging import Logger
|
|
3
|
+
from pypomes_core import APP_PREFIX, env_get_int, env_get_str
|
|
5
4
|
from typing import Any
|
|
6
5
|
|
|
7
6
|
from .iam_common import (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
_IAM_SERVERS, IamServer, IamParam, _iam_lock
|
|
8
|
+
)
|
|
9
|
+
from .iam_actions import action_token
|
|
10
|
+
from .iam_services import (
|
|
11
|
+
service_login, service_logout, service_callback, service_exchange, service_token
|
|
11
12
|
)
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# log the request
|
|
31
|
-
if logger:
|
|
32
|
-
logger.debug(msg=_log_init(request=request))
|
|
33
|
-
|
|
34
|
-
# obtain the login URL
|
|
35
|
-
login_data: dict[str, str] = _service_login(registry=registry,
|
|
36
|
-
args=request.args,
|
|
37
|
-
logger=logger)
|
|
38
|
-
result = jsonify(login_data)
|
|
39
|
-
|
|
40
|
-
# log the response
|
|
41
|
-
if logger:
|
|
42
|
-
logger.debug(msg=f"Response {result}")
|
|
43
|
-
|
|
44
|
-
return result
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# @flask_app.route(rule=<logout_endpoint>, # JUSBR_LOGOUT_ENDPOINT: /iam/jusbr:logout
|
|
48
|
-
# methods=["GET"])
|
|
49
|
-
# @flask_app.route(rule=<login_endpoint>, # KEYCLOAK_LOGOUT_ENDPOINT: /iam/keycloak:logout
|
|
50
|
-
# methods=["GET"])
|
|
51
|
-
def service_logout() -> Response:
|
|
15
|
+
def iam_setup(flask_app: Flask,
|
|
16
|
+
iam_server: IamServer,
|
|
17
|
+
base_url: str,
|
|
18
|
+
client_id: str,
|
|
19
|
+
client_realm: str,
|
|
20
|
+
client_secret: str | None,
|
|
21
|
+
recipient_attribute: str,
|
|
22
|
+
admin_id: str = None,
|
|
23
|
+
admin_secret: str = None,
|
|
24
|
+
login_timeout: int = None,
|
|
25
|
+
public_key_lifetime: int = None,
|
|
26
|
+
callback_endpoint: str = None,
|
|
27
|
+
exchange_endpoint: str = None,
|
|
28
|
+
login_endpoint: str = None,
|
|
29
|
+
logout_endpoint: str = None,
|
|
30
|
+
token_endpoint: str = None) -> None:
|
|
52
31
|
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
32
|
+
Establish the provided parameters for configuring the *IAM* server *iam_server*.
|
|
33
|
+
|
|
34
|
+
The parameters *admin_id* and *admin_* are required only if administrative are task are planned.
|
|
35
|
+
The optional parameter *client_timeout* refers to the maximum time in seconds allowed for the
|
|
36
|
+
user to login at the *IAM* server's login page, and defaults to no time limit.
|
|
37
|
+
|
|
38
|
+
The parameter *client_secret* is required in most requests to the *IAM* server. In the case
|
|
39
|
+
it is not provided, but *admin_id* and *admin_secret* are, it is obtained from the *IAM* server itself
|
|
40
|
+
the first time it is needed.
|
|
41
|
+
|
|
42
|
+
:param flask_app: the Flask application
|
|
43
|
+
:param iam_server: identifies the supported *IAM* server (currently, *jusbr* or *keycloak*)
|
|
44
|
+
:param base_url: base URL to request services
|
|
45
|
+
:param client_id: the client's identification with the *IAM* server
|
|
46
|
+
:param client_realm: the client realm
|
|
47
|
+
:param client_secret: the client's password with the *IAM* server
|
|
48
|
+
:param recipient_attribute: attribute in the token's payload holding the token's subject
|
|
49
|
+
:param admin_id: identifies the realm administrator
|
|
50
|
+
:param admin_secret: password for the realm administrator
|
|
51
|
+
:param login_timeout: timeout for login authentication (in seconds,defaults to no timeout)
|
|
52
|
+
:param public_key_lifetime: how long to use *IAM* server's public key, before refreshing it (in seconds)
|
|
53
|
+
:param callback_endpoint: endpoint for the callback from the front end
|
|
54
|
+
:param exchange_endpoint: endpoint for requesting token exchange
|
|
55
|
+
:param login_endpoint: endpoint for redirecting user to the *IAM* server's login page
|
|
56
|
+
:param logout_endpoint: endpoint for terminating user access
|
|
57
|
+
:param token_endpoint: endpoint for retrieving authentication token
|
|
58
58
|
"""
|
|
59
|
-
# retrieve logger and registry
|
|
60
|
-
registry: dict[str, Any] = __get_iam_registry(endpoint=request.endpoint)
|
|
61
|
-
logger: Logger = registry["logger"]
|
|
62
|
-
|
|
63
|
-
# log the request
|
|
64
|
-
if logger:
|
|
65
|
-
logger.debug(msg=_log_init(request=request))
|
|
66
|
-
|
|
67
|
-
# logout the user
|
|
68
|
-
_service_logout(registry=registry,
|
|
69
|
-
args=request.args,
|
|
70
|
-
logger=logger)
|
|
71
|
-
|
|
72
|
-
result: Response = Response(status=200)
|
|
73
|
-
|
|
74
|
-
# log the response
|
|
75
|
-
if logger:
|
|
76
|
-
logger.debug(msg=f"Response {result}")
|
|
77
|
-
|
|
78
|
-
return result
|
|
79
59
|
|
|
60
|
+
# configure the Keycloak registry
|
|
61
|
+
with _iam_lock:
|
|
62
|
+
_IAM_SERVERS[iam_server] = {
|
|
63
|
+
IamParam.URL_BASE: base_url,
|
|
64
|
+
IamParam.CLIENT_ID: client_id,
|
|
65
|
+
IamParam.CLIENT_REALM: client_realm,
|
|
66
|
+
IamParam.CLIENT_SECRET: client_secret,
|
|
67
|
+
IamParam.RECIPIENT_ATTR: recipient_attribute,
|
|
68
|
+
IamParam.ADMIN_ID: admin_id,
|
|
69
|
+
IamParam.ADMIN_SECRET: admin_secret,
|
|
70
|
+
IamParam.LOGIN_TIMEOUT: login_timeout,
|
|
71
|
+
IamParam.PK_LIFETIME: public_key_lifetime,
|
|
72
|
+
IamParam.PK_EXPIRATION: 0,
|
|
73
|
+
IamParam.PUBLIC_KEY: None,
|
|
74
|
+
IamParam.USERS: {}
|
|
75
|
+
}
|
|
80
76
|
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
77
|
+
# establish the endpoints
|
|
78
|
+
if callback_endpoint:
|
|
79
|
+
flask_app.add_url_rule(rule=callback_endpoint,
|
|
80
|
+
endpoint=f"{iam_server}-callback",
|
|
81
|
+
view_func=service_callback,
|
|
82
|
+
methods=["GET"])
|
|
83
|
+
if login_endpoint:
|
|
84
|
+
flask_app.add_url_rule(rule=login_endpoint,
|
|
85
|
+
endpoint=f"{iam_server}-login",
|
|
86
|
+
view_func=service_login,
|
|
87
|
+
methods=["GET"])
|
|
88
|
+
if logout_endpoint:
|
|
89
|
+
flask_app.add_url_rule(rule=logout_endpoint,
|
|
90
|
+
endpoint=f"{iam_server}-logout",
|
|
91
|
+
view_func=service_logout,
|
|
92
|
+
methods=["GET"])
|
|
93
|
+
if token_endpoint:
|
|
94
|
+
flask_app.add_url_rule(rule=token_endpoint,
|
|
95
|
+
endpoint=f"{iam_server}-token",
|
|
96
|
+
view_func=service_token,
|
|
97
|
+
methods=["GET"])
|
|
98
|
+
if exchange_endpoint:
|
|
99
|
+
flask_app.add_url_rule(rule=exchange_endpoint,
|
|
100
|
+
endpoint=f"{iam_server}-exchange",
|
|
101
|
+
view_func=service_exchange,
|
|
102
|
+
methods=["POST"])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def iam_get_env_parameters(iam_prefix: str = None) -> dict[str, Any]:
|
|
86
106
|
"""
|
|
87
|
-
|
|
107
|
+
Retrieve the set parameters for a *IAM* server from the environment.
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
the parameters are returned ready to be used as a '**kwargs' parameter set in a call to *iam_setup()*,
|
|
110
|
+
and sorted in the order appropriate to use them instead with a '*args' parameter set.
|
|
91
111
|
|
|
92
|
-
:
|
|
112
|
+
:param iam_prefix: the prefix classifying the parameters
|
|
113
|
+
:return: the sorted parameters classified by *prefix*
|
|
93
114
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
"client_secret": keycloak_registry["client-secret"],
|
|
117
|
-
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
|
|
118
|
-
"audience": token_data[0]
|
|
119
|
-
}
|
|
120
|
-
exchange_url = f"{keycloak_registry['base-url']}/protocol/openid-connect/token"
|
|
121
|
-
if logger:
|
|
122
|
-
logger.debug(msg=f"POST '{exchange_url}', data {json.dumps(obj=payload,
|
|
123
|
-
ensure_ascii=False)}")
|
|
124
|
-
headers: dict[str, str] = {
|
|
125
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
126
|
-
}
|
|
127
|
-
response: requests.Response = requests.post(url=exchange_url,
|
|
128
|
-
data=payload,
|
|
129
|
-
headers=headers)
|
|
130
|
-
if response.status_code == 200:
|
|
131
|
-
# request succeeded
|
|
132
|
-
if logger:
|
|
133
|
-
logger.debug(msg=f"POST success, status {response.status_code}")
|
|
134
|
-
reply: dict[str, Any] = response.json()
|
|
135
|
-
token_data = (token_data[0], reply.get("access_token"))
|
|
136
|
-
else:
|
|
137
|
-
# request resulted in error
|
|
138
|
-
err_msg = f"POST failure, status {response.status_code}, reason '{response.reason}'"
|
|
139
|
-
if hasattr(response, "content") and response.content:
|
|
140
|
-
err_msg += f", content '{response.content}'"
|
|
141
|
-
errors.append(err_msg)
|
|
142
|
-
|
|
143
|
-
result: Response
|
|
144
|
-
if errors:
|
|
145
|
-
result = jsonify({"errors": "; ".join(errors)})
|
|
146
|
-
result.status_code = 400
|
|
147
|
-
else:
|
|
148
|
-
result = jsonify({
|
|
149
|
-
"user-id": token_data[0],
|
|
150
|
-
"access-token": token_data[1]})
|
|
151
|
-
|
|
152
|
-
# log the response
|
|
153
|
-
if logger:
|
|
154
|
-
logger.debug(msg=f"Response {result}")
|
|
155
|
-
|
|
156
|
-
return result
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
# @flask_app.route(rule=<token_endpoint>, # JUSBR_TOKEN_ENDPOINT: /iam/jusbr:get-token
|
|
160
|
-
# methods=["GET"])
|
|
161
|
-
# @flask_app.route(rule=<token_endpoint>, # JUSBR_TOKEN_ENDPOINT: /iam/jusbr:get-token
|
|
162
|
-
# methods=["GET"])
|
|
163
|
-
def service_token() -> Response:
|
|
115
|
+
return {
|
|
116
|
+
"base_url": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_URL_AUTH_BASE"),
|
|
117
|
+
"client_id": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_CLIENT_ID"),
|
|
118
|
+
"client_realm": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_CLIENT_REALM"),
|
|
119
|
+
"client_secret": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_CLIENT_SECRET"),
|
|
120
|
+
"recipient_attribute": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_RECIPIENT_ATTR"),
|
|
121
|
+
"admin_id": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ADMIN_ID"),
|
|
122
|
+
"admin_secret": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ADMIN_SECRET"),
|
|
123
|
+
"login_timeout": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_LOGIN_TIMEOUT"),
|
|
124
|
+
"public_key_lifetime": env_get_int(key=f"{APP_PREFIX}_{iam_prefix}_PUBLIC_KEY_LIFETIME"),
|
|
125
|
+
"callback_endpoint": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ENDPOINT_CALLBACK"),
|
|
126
|
+
"exchange_endpoint": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ENDPOINT_EXCHANGE"),
|
|
127
|
+
"login_endpoint": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ENDPOINT_LOGIN"),
|
|
128
|
+
"logout_endpoint": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ENDPOINT_LOGOUT"),
|
|
129
|
+
"token_endpoint": env_get_str(key=f"{APP_PREFIX}_{iam_prefix}_ENDPOINT_TOKEN")
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def iam_get_token(iam_server: IamServer,
|
|
134
|
+
user_id: str,
|
|
135
|
+
errors: list[str] = None,
|
|
136
|
+
logger: Logger = None) -> str:
|
|
164
137
|
"""
|
|
165
|
-
|
|
138
|
+
Retrieve an authentication token for *user_id*.
|
|
166
139
|
|
|
167
|
-
:
|
|
140
|
+
:param iam_server: identifies the *IAM* server
|
|
141
|
+
:param user_id: identifies the user
|
|
142
|
+
:param errors: incidental errors
|
|
143
|
+
:param logger: optional logger
|
|
144
|
+
:return: the uthentication tokem
|
|
168
145
|
"""
|
|
169
|
-
#
|
|
170
|
-
|
|
171
|
-
logger: Logger = registry["logger"]
|
|
172
|
-
|
|
173
|
-
# log the request
|
|
174
|
-
if logger:
|
|
175
|
-
logger.debug(msg=_log_init(request=request))
|
|
146
|
+
# declare the return variable
|
|
147
|
+
result: str
|
|
176
148
|
|
|
177
149
|
# retrieve the token
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if token:
|
|
185
|
-
result = jsonify({"token": token})
|
|
186
|
-
else:
|
|
187
|
-
result = Response("; ".join(errors))
|
|
188
|
-
result.status_code = 401
|
|
189
|
-
|
|
190
|
-
# log the response
|
|
191
|
-
if logger:
|
|
192
|
-
logger.debug(msg=f"Response {result}")
|
|
193
|
-
|
|
150
|
+
args: dict[str, Any] = {"user-id": user_id}
|
|
151
|
+
with _iam_lock:
|
|
152
|
+
result = action_token(iam_server=iam_server,
|
|
153
|
+
args=args,
|
|
154
|
+
errors=errors,
|
|
155
|
+
logger=logger)
|
|
194
156
|
return result
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def __get_iam_registry(endpoint: str) -> dict[str, Any]:
|
|
198
|
-
"""
|
|
199
|
-
Retrieve the registry associated the the IAM identifies by *endpoint*.
|
|
200
|
-
|
|
201
|
-
:param endpoint: the service enpoint identifying the IAM.
|
|
202
|
-
:return: the tuple (*logger*, *registry*) associated with *endpoint*
|
|
203
|
-
"""
|
|
204
|
-
# initialize the return variable
|
|
205
|
-
result: dict[str, Any] | None = None
|
|
206
|
-
|
|
207
|
-
if endpoint.startswith("jusbr-"):
|
|
208
|
-
result = IAM_SERVERS[IamServer.IAM_JUSRBR]
|
|
209
|
-
elif endpoint.startswith("keycloak-"):
|
|
210
|
-
result = IAM_SERVERS[IamServer.IAM_KEYCLOAK]
|
|
211
|
-
|
|
212
|
-
return result
|
|
213
|
-
|