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.
- pypomes_iam/__init__.py +15 -15
- pypomes_iam/iam_actions.py +144 -40
- pypomes_iam/iam_common.py +38 -42
- pypomes_iam/iam_pomes.py +33 -23
- pypomes_iam/iam_services.py +124 -50
- pypomes_iam/provider_pomes.py +77 -68
- {pypomes_iam-0.8.0.dist-info → pypomes_iam-0.8.9.dist-info}/METADATA +1 -1
- pypomes_iam-0.8.9.dist-info/RECORD +11 -0
- pypomes_iam-0.8.0.dist-info/RECORD +0 -11
- {pypomes_iam-0.8.0.dist-info → pypomes_iam-0.8.9.dist-info}/WHEEL +0 -0
- {pypomes_iam-0.8.0.dist-info → pypomes_iam-0.8.9.dist-info}/licenses/LICENSE +0 -0
pypomes_iam/provider_pomes.py
CHANGED
|
@@ -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
|
-
|
|
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[
|
|
44
|
+
def __get_provider_data() -> dict[IamProvider, dict[ProviderParam, Any]]:
|
|
40
45
|
"""
|
|
41
|
-
Obtain the configuration data for select *
|
|
46
|
+
Obtain the configuration data for select *IAM* providers.
|
|
42
47
|
|
|
43
|
-
The configuration parameters for the
|
|
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>
|
|
48
|
-
below for each providers, where *<
|
|
49
|
-
- *<APP_PREFIX>_<
|
|
50
|
-
- *<APP_PREFIX>_<
|
|
51
|
-
- *<APP_PREFIX>_<
|
|
52
|
-
- *<APP_PREFIX>_<
|
|
53
|
-
- *<APP_PREFIX>_<
|
|
54
|
-
- *<APP_PREFIX>_<
|
|
55
|
-
|
|
56
|
-
2. The special environment variable *<APP_PREFIX>
|
|
57
|
-
to obtain JWT tokens. It is not part of the *JWT* providers' setup, but is meant to be
|
|
58
|
-
by function *provider_setup_endpoint()*, wherein the value in that variable would represent
|
|
59
|
-
default value for its parameter.
|
|
60
|
-
|
|
61
|
-
:return: the configuration data for the select *
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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.
|
|
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
|
-
# "
|
|
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
|
-
# "
|
|
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[
|
|
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
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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 *
|
|
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']),
|
|
126
|
-
added to the request headers, may be specified in *headers_data*. Likewise, optional constant
|
|
127
|
-
(such as ['grant_type', 'client_credentials']), to be added to the request body,
|
|
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
|
|
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
|
|
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 =
|
|
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 "
|
|
155
|
-
|
|
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[
|
|
159
|
-
ProviderParam.
|
|
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.
|
|
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}
|
|
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"
|
|
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>,
|
|
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
|
-
|
|
228
|
-
|
|
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 =
|
|
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
|
|
238
|
-
token: str = provider_get_token(
|
|
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 = "
|
|
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(
|
|
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
|
|
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(
|
|
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.
|
|
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
|
|
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 '{
|
|
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.
|
|
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,,
|
|
File without changes
|
|
File without changes
|