digitalhub 0.13.4__py3-none-any.whl → 0.14.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 digitalhub might be problematic. Click here for more details.
- digitalhub/__init__.py +3 -8
- digitalhub/context/api.py +1 -5
- digitalhub/context/builder.py +1 -5
- digitalhub/context/context.py +2 -9
- digitalhub/entities/_base/_base/entity.py +0 -15
- digitalhub/entities/_base/context/entity.py +1 -1
- digitalhub/entities/_base/entity/builder.py +5 -5
- digitalhub/entities/_base/entity/entity.py +0 -8
- digitalhub/entities/_base/executable/entity.py +169 -79
- digitalhub/entities/_base/material/entity.py +6 -22
- digitalhub/entities/_base/material/utils.py +1 -4
- digitalhub/entities/_base/runtime_entity/builder.py +53 -18
- digitalhub/entities/_base/unversioned/entity.py +1 -1
- digitalhub/entities/_base/versioned/entity.py +1 -1
- digitalhub/entities/_commons/enums.py +1 -31
- digitalhub/entities/_commons/utils.py +83 -21
- digitalhub/entities/_constructors/_resources.py +151 -0
- digitalhub/entities/{_base/entity/_constructors → _constructors}/name.py +18 -0
- digitalhub/entities/_processors/base/__init__.py +3 -0
- digitalhub/entities/_processors/{base.py → base/crud.py} +14 -226
- digitalhub/entities/_processors/base/import_export.py +123 -0
- digitalhub/entities/_processors/base/processor.py +302 -0
- digitalhub/entities/_processors/base/special_ops.py +108 -0
- digitalhub/entities/_processors/context/__init__.py +3 -0
- digitalhub/entities/_processors/context/crud.py +652 -0
- digitalhub/entities/_processors/context/import_export.py +242 -0
- digitalhub/entities/_processors/context/material.py +123 -0
- digitalhub/entities/_processors/context/processor.py +400 -0
- digitalhub/entities/_processors/context/special_ops.py +476 -0
- digitalhub/entities/_processors/processors.py +12 -0
- digitalhub/entities/_processors/utils.py +12 -11
- digitalhub/entities/artifact/crud.py +58 -22
- digitalhub/entities/artifact/utils.py +3 -3
- digitalhub/entities/dataitem/crud.py +63 -20
- digitalhub/entities/dataitem/table/entity.py +24 -22
- digitalhub/entities/dataitem/utils.py +15 -15
- digitalhub/entities/function/_base/entity.py +3 -3
- digitalhub/entities/function/crud.py +55 -24
- digitalhub/entities/model/_base/entity.py +62 -20
- digitalhub/entities/model/crud.py +58 -22
- digitalhub/entities/model/utils.py +3 -3
- digitalhub/entities/project/_base/entity.py +321 -152
- digitalhub/entities/project/crud.py +15 -23
- digitalhub/entities/run/_base/builder.py +0 -4
- digitalhub/entities/run/_base/entity.py +70 -63
- digitalhub/entities/run/crud.py +79 -26
- digitalhub/entities/secret/_base/entity.py +1 -5
- digitalhub/entities/secret/crud.py +29 -26
- digitalhub/entities/task/_base/builder.py +0 -4
- digitalhub/entities/task/_base/entity.py +5 -5
- digitalhub/entities/task/_base/models.py +13 -16
- digitalhub/entities/task/crud.py +61 -29
- digitalhub/entities/trigger/_base/entity.py +1 -5
- digitalhub/entities/trigger/crud.py +64 -24
- digitalhub/entities/workflow/_base/entity.py +3 -3
- digitalhub/entities/workflow/crud.py +55 -21
- digitalhub/factory/entity.py +283 -0
- digitalhub/factory/enums.py +18 -0
- digitalhub/factory/registry.py +197 -0
- digitalhub/factory/runtime.py +44 -0
- digitalhub/factory/utils.py +3 -54
- digitalhub/runtimes/_base.py +2 -2
- digitalhub/stores/client/_base/enums.py +39 -0
- digitalhub/stores/client/_base/key_builder.py +2 -2
- digitalhub/stores/client/_base/params_builder.py +48 -0
- digitalhub/stores/client/api.py +6 -10
- digitalhub/stores/client/builder.py +4 -4
- digitalhub/stores/client/dhcore/api_builder.py +2 -1
- digitalhub/stores/client/dhcore/client.py +85 -429
- digitalhub/stores/client/dhcore/configurator.py +109 -328
- digitalhub/stores/client/dhcore/enums.py +0 -16
- digitalhub/stores/client/dhcore/error_parser.py +0 -4
- digitalhub/stores/client/dhcore/header_manager.py +61 -0
- digitalhub/stores/client/dhcore/http_handler.py +133 -0
- digitalhub/stores/client/dhcore/params_builder.py +147 -134
- digitalhub/stores/client/dhcore/response_processor.py +102 -0
- digitalhub/stores/client/dhcore/utils.py +6 -72
- digitalhub/stores/client/local/api_builder.py +1 -1
- digitalhub/stores/client/local/client.py +79 -47
- digitalhub/stores/client/local/params_builder.py +18 -41
- digitalhub/stores/credentials/api.py +0 -4
- digitalhub/stores/credentials/configurator.py +2 -28
- digitalhub/stores/credentials/enums.py +3 -0
- digitalhub/stores/credentials/handler.py +0 -12
- digitalhub/stores/credentials/ini_module.py +0 -22
- digitalhub/stores/credentials/store.py +0 -4
- digitalhub/stores/data/_base/store.py +0 -16
- digitalhub/stores/data/builder.py +1 -5
- digitalhub/stores/data/local/store.py +0 -103
- digitalhub/stores/data/remote/store.py +0 -4
- digitalhub/stores/data/s3/configurator.py +60 -14
- digitalhub/stores/data/s3/store.py +49 -16
- digitalhub/stores/data/sql/configurator.py +0 -8
- digitalhub/stores/data/sql/store.py +21 -10
- digitalhub/stores/readers/data/factory.py +0 -8
- digitalhub/stores/readers/data/pandas/reader.py +0 -16
- digitalhub/utils/file_utils.py +0 -17
- digitalhub/utils/generic_utils.py +0 -12
- digitalhub/utils/git_utils.py +0 -8
- digitalhub/utils/io_utils.py +0 -12
- digitalhub/utils/store_utils.py +44 -0
- {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/METADATA +3 -2
- {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/RECORD +111 -95
- digitalhub/entities/_processors/context.py +0 -1450
- digitalhub/entities/task/_base/utils.py +0 -22
- digitalhub/factory/factory.py +0 -381
- digitalhub/stores/client/dhcore/models.py +0 -40
- digitalhub/stores/data/s3/utils.py +0 -78
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/__init__.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/metadata.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/spec.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/status.py +0 -0
- /digitalhub/entities/{_base/entity/_constructors → _constructors}/uuid.py +0 -0
- {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/WHEEL +0 -0
- {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/licenses/AUTHORS +0 -0
- {digitalhub-0.13.4.dist-info → digitalhub-0.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -22,54 +22,32 @@ if typing.TYPE_CHECKING:
|
|
|
22
22
|
|
|
23
23
|
class ClientDHCoreConfigurator(Configurator):
|
|
24
24
|
"""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
Once the credentials are read, the configurator check the current profile
|
|
37
|
-
name from the ini file, and set it. The default one is __default. The
|
|
38
|
-
profile is used to discriminate a set of credentials inside the ini file.
|
|
39
|
-
|
|
40
|
-
The configurator finally set the authentication type based on the credentials.
|
|
41
|
-
The logic is the following:
|
|
42
|
-
|
|
43
|
-
1. Check for a personal access token. Use it immediately to
|
|
44
|
-
require a timed access token in an exchange endpoint.
|
|
45
|
-
Switche then the origin to file and .
|
|
46
|
-
Set the auth type to EXCHANGE.
|
|
47
|
-
2. Check for an access token and a refresh token.
|
|
48
|
-
Set the auth type to OAUTH2.
|
|
49
|
-
3. Check for username and password.
|
|
50
|
-
Set the auth type to BASIC.
|
|
51
|
-
4. If none of the above is true, leave the auth type to None.
|
|
25
|
+
DHCore client configurator for credential management and authentication.
|
|
26
|
+
|
|
27
|
+
Handles loading credentials from environment variables and configuration files,
|
|
28
|
+
evaluates authentication types, and manages token refresh operations. Supports
|
|
29
|
+
multiple authentication methods: EXCHANGE (personal access token), OAUTH2
|
|
30
|
+
(access + refresh tokens), ACCESS_TOKEN (access token only), and BASIC
|
|
31
|
+
(username + password).
|
|
32
|
+
|
|
33
|
+
The configurator automatically determines the best authentication method and
|
|
34
|
+
handles token exchange for personal access tokens by switching to file-based
|
|
35
|
+
credential storage.
|
|
52
36
|
"""
|
|
53
37
|
|
|
54
38
|
keys = [*list_enum(CredsEnvVar)]
|
|
55
39
|
required_keys = [CredsEnvVar.DHCORE_ENDPOINT.value]
|
|
56
|
-
|
|
40
|
+
keys_to_prefix = [
|
|
57
41
|
CredsEnvVar.DHCORE_REFRESH_TOKEN.value,
|
|
58
42
|
CredsEnvVar.DHCORE_ACCESS_TOKEN.value,
|
|
59
43
|
CredsEnvVar.DHCORE_ISSUER.value,
|
|
60
44
|
CredsEnvVar.DHCORE_CLIENT_ID.value,
|
|
45
|
+
CredsEnvVar.OAUTH2_TOKEN_ENDPOINT.value,
|
|
61
46
|
]
|
|
62
47
|
|
|
63
48
|
def __init__(self) -> None:
|
|
64
49
|
"""
|
|
65
|
-
Initialize
|
|
66
|
-
|
|
67
|
-
Sets up the configurator by calling the parent constructor and
|
|
68
|
-
initializing the authentication type evaluation process.
|
|
69
|
-
|
|
70
|
-
Returns
|
|
71
|
-
-------
|
|
72
|
-
None
|
|
50
|
+
Initialize DHCore configurator and evaluate authentication type.
|
|
73
51
|
"""
|
|
74
52
|
super().__init__()
|
|
75
53
|
self._auth_type: str | None = None
|
|
@@ -81,20 +59,10 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
81
59
|
|
|
82
60
|
def load_env_vars(self) -> None:
|
|
83
61
|
"""
|
|
84
|
-
Load credentials from environment variables.
|
|
85
|
-
|
|
86
|
-
Retrieves DHCore credentials from environment variables, sanitizes
|
|
87
|
-
them (particularly endpoint URLs), and stores them in the credentials
|
|
88
|
-
handler for the environment origin.
|
|
62
|
+
Load and sanitize credentials from environment variables.
|
|
89
63
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
None
|
|
93
|
-
|
|
94
|
-
Notes
|
|
95
|
-
-----
|
|
96
|
-
This method sanitizes endpoint and issuer URLs to ensure they have
|
|
97
|
-
proper schemes and removes trailing slashes.
|
|
64
|
+
Sanitizes endpoint and issuer URLs to ensure proper HTTP/HTTPS schemes
|
|
65
|
+
and removes trailing slashes.
|
|
98
66
|
"""
|
|
99
67
|
env_creds = self._creds_handler.load_from_env(self.keys)
|
|
100
68
|
env_creds = self._sanitize_env_vars(env_creds)
|
|
@@ -102,30 +70,25 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
102
70
|
|
|
103
71
|
def _sanitize_env_vars(self, creds: dict) -> dict:
|
|
104
72
|
"""
|
|
105
|
-
Sanitize
|
|
73
|
+
Sanitize environment variable credentials.
|
|
106
74
|
|
|
107
|
-
Validates and normalizes endpoint and issuer URLs
|
|
108
|
-
|
|
75
|
+
Validates and normalizes endpoint and issuer URLs. Environment variables
|
|
76
|
+
use full "DHCORE_" prefixes.
|
|
109
77
|
|
|
110
78
|
Parameters
|
|
111
79
|
----------
|
|
112
80
|
creds : dict
|
|
113
|
-
Raw credentials
|
|
81
|
+
Raw credentials from environment variables.
|
|
114
82
|
|
|
115
83
|
Returns
|
|
116
84
|
-------
|
|
117
85
|
dict
|
|
118
|
-
Sanitized credentials
|
|
86
|
+
Sanitized credentials with normalized URLs.
|
|
119
87
|
|
|
120
88
|
Raises
|
|
121
89
|
------
|
|
122
90
|
ClientError
|
|
123
91
|
If endpoint or issuer URLs have invalid schemes.
|
|
124
|
-
|
|
125
|
-
Notes
|
|
126
|
-
-----
|
|
127
|
-
Environment variables are expected to have the full "DHCORE_" prefix
|
|
128
|
-
for issuer endpoints.
|
|
129
92
|
"""
|
|
130
93
|
creds[CredsEnvVar.DHCORE_ENDPOINT.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ENDPOINT.value])
|
|
131
94
|
creds[CredsEnvVar.DHCORE_ISSUER.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ISSUER.value])
|
|
@@ -133,112 +96,74 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
133
96
|
|
|
134
97
|
def load_file_vars(self) -> None:
|
|
135
98
|
"""
|
|
136
|
-
Load credentials from configuration file.
|
|
137
|
-
|
|
138
|
-
Retrieves DHCore credentials from the .dhcore.ini file, handles
|
|
139
|
-
compatibility with CLI format (keys without DHCORE_ prefix), and
|
|
140
|
-
falls back to environment variables for missing endpoint and
|
|
141
|
-
personal access token values.
|
|
142
|
-
|
|
143
|
-
Returns
|
|
144
|
-
-------
|
|
145
|
-
None
|
|
99
|
+
Load credentials from configuration file with CLI compatibility.
|
|
146
100
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
This method handles the case where:
|
|
150
|
-
- Endpoint might not be present in file response, falls back to env
|
|
151
|
-
- Personal access token might not be present, falls back to env
|
|
152
|
-
- File format uses keys without "DHCORE_" prefix for compatibility
|
|
101
|
+
Handles keys without "DHCORE_" prefix for CLI compatibility. Falls back
|
|
102
|
+
to environment variables for missing endpoint and personal access token values.
|
|
153
103
|
"""
|
|
154
|
-
|
|
155
|
-
file_creds = self._creds_handler.load_from_file(keys)
|
|
156
|
-
env_creds = self._creds_handler.load_from_env(self.keys)
|
|
157
|
-
|
|
158
|
-
# Because in the response there is no endpoint
|
|
159
|
-
if file_creds[CredsEnvVar.DHCORE_ENDPOINT.value] is None:
|
|
160
|
-
file_creds[CredsEnvVar.DHCORE_ENDPOINT.value] = env_creds.get(CredsEnvVar.DHCORE_ENDPOINT.value)
|
|
104
|
+
file_creds = self._creds_handler.load_from_file(self.keys)
|
|
161
105
|
|
|
162
106
|
# Because in the response there is no personal access token
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
107
|
+
pat = CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value
|
|
108
|
+
if file_creds[pat] is None:
|
|
109
|
+
file_creds[pat] = self._creds_handler.load_from_env([pat]).get(pat)
|
|
110
|
+
|
|
111
|
+
# Because in the response there is no endpoint
|
|
112
|
+
endpoint = CredsEnvVar.DHCORE_ENDPOINT.value
|
|
113
|
+
if file_creds[endpoint] is None:
|
|
114
|
+
file_creds[endpoint] = self._creds_handler.load_from_env([endpoint]).get(endpoint)
|
|
167
115
|
|
|
168
116
|
file_creds = self._sanitize_file_vars(file_creds)
|
|
169
117
|
self._creds_handler.set_credentials(self._file, file_creds)
|
|
170
118
|
|
|
171
119
|
def _sanitize_file_vars(self, creds: dict) -> dict:
|
|
172
120
|
"""
|
|
173
|
-
Sanitize
|
|
121
|
+
Sanitize configuration file credentials.
|
|
174
122
|
|
|
175
|
-
Handles
|
|
176
|
-
|
|
177
|
-
|
|
123
|
+
Handles different key formats between file and environment variables.
|
|
124
|
+
File format omits "DHCORE_" prefix for: issuer, client_id, access_token,
|
|
125
|
+
refresh_token. Full names used for: endpoint, user, password, personal_access_token.
|
|
178
126
|
|
|
179
127
|
Parameters
|
|
180
128
|
----------
|
|
181
129
|
creds : dict
|
|
182
|
-
Raw credentials
|
|
130
|
+
Raw credentials from configuration file.
|
|
183
131
|
|
|
184
132
|
Returns
|
|
185
133
|
-------
|
|
186
134
|
dict
|
|
187
|
-
Sanitized credentials
|
|
188
|
-
and normalized URLs, filtered to include only valid keys.
|
|
135
|
+
Sanitized credentials with standardized keys and normalized URLs.
|
|
189
136
|
|
|
190
137
|
Raises
|
|
191
138
|
------
|
|
192
139
|
ClientError
|
|
193
140
|
If endpoint or issuer URLs have invalid schemes.
|
|
194
|
-
|
|
195
|
-
Notes
|
|
196
|
-
-----
|
|
197
|
-
File format expects these keys without "DHCORE_" prefix:
|
|
198
|
-
- issuer, client_id, access_token, refresh_token
|
|
199
|
-
But uses full names for: endpoint, user, password, personal_access_token
|
|
200
141
|
"""
|
|
201
142
|
creds[CredsEnvVar.DHCORE_ENDPOINT.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ENDPOINT.value])
|
|
202
|
-
creds[CredsEnvVar.DHCORE_ISSUER.value] = self._sanitize_endpoint(
|
|
203
|
-
creds[CredsEnvVar.DHCORE_ISSUER.value.removeprefix("DHCORE_")]
|
|
204
|
-
)
|
|
205
|
-
creds[CredsEnvVar.DHCORE_REFRESH_TOKEN.value] = creds[
|
|
206
|
-
CredsEnvVar.DHCORE_REFRESH_TOKEN.value.removeprefix("DHCORE_")
|
|
207
|
-
]
|
|
208
|
-
creds[CredsEnvVar.DHCORE_ACCESS_TOKEN.value] = creds[
|
|
209
|
-
CredsEnvVar.DHCORE_ACCESS_TOKEN.value.removeprefix("DHCORE_")
|
|
210
|
-
]
|
|
211
|
-
creds[CredsEnvVar.DHCORE_CLIENT_ID.value] = creds[CredsEnvVar.DHCORE_CLIENT_ID.value.removeprefix("DHCORE_")]
|
|
143
|
+
creds[CredsEnvVar.DHCORE_ISSUER.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ISSUER.value])
|
|
212
144
|
return {k: v for k, v in creds.items() if k in self.keys}
|
|
213
145
|
|
|
214
146
|
@staticmethod
|
|
215
147
|
def _sanitize_endpoint(endpoint: str | None = None) -> str | None:
|
|
216
148
|
"""
|
|
217
|
-
|
|
149
|
+
Validate and normalize endpoint URL.
|
|
218
150
|
|
|
219
|
-
|
|
220
|
-
trims whitespace, and removes trailing slashes for consistency.
|
|
151
|
+
Ensures proper HTTP/HTTPS scheme, trims whitespace, and removes trailing slashes.
|
|
221
152
|
|
|
222
153
|
Parameters
|
|
223
154
|
----------
|
|
224
|
-
endpoint : str
|
|
225
|
-
|
|
155
|
+
endpoint : str
|
|
156
|
+
Endpoint URL to sanitize.
|
|
226
157
|
|
|
227
158
|
Returns
|
|
228
159
|
-------
|
|
229
160
|
str or None
|
|
230
|
-
|
|
231
|
-
or None if input was None.
|
|
161
|
+
Sanitized URL or None if input was None.
|
|
232
162
|
|
|
233
163
|
Raises
|
|
234
164
|
------
|
|
235
165
|
ClientError
|
|
236
|
-
If
|
|
237
|
-
|
|
238
|
-
Notes
|
|
239
|
-
-----
|
|
240
|
-
This method ensures endpoint URLs are properly formatted for
|
|
241
|
-
HTTP requests and prevents common URL formatting issues.
|
|
166
|
+
If endpoint lacks http:// or https:// scheme.
|
|
242
167
|
"""
|
|
243
168
|
if endpoint is None:
|
|
244
169
|
return
|
|
@@ -252,23 +177,17 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
252
177
|
"""
|
|
253
178
|
Get the configured DHCore backend endpoint.
|
|
254
179
|
|
|
255
|
-
|
|
256
|
-
(environment or file based on current origin).
|
|
180
|
+
Returns the sanitized and validated endpoint URL from current credential source.
|
|
257
181
|
|
|
258
182
|
Returns
|
|
259
183
|
-------
|
|
260
184
|
str
|
|
261
|
-
|
|
185
|
+
DHCore backend endpoint URL.
|
|
262
186
|
|
|
263
187
|
Raises
|
|
264
188
|
------
|
|
265
189
|
KeyError
|
|
266
|
-
If
|
|
267
|
-
|
|
268
|
-
Notes
|
|
269
|
-
-----
|
|
270
|
-
The endpoint returned is already sanitized and validated during
|
|
271
|
-
the credential loading process.
|
|
190
|
+
If endpoint not configured in current credential source.
|
|
272
191
|
"""
|
|
273
192
|
creds = self._creds_handler.get_credentials(self._origin)
|
|
274
193
|
return creds[CredsEnvVar.DHCORE_ENDPOINT.value]
|
|
@@ -279,22 +198,10 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
279
198
|
|
|
280
199
|
def change_origin(self) -> None:
|
|
281
200
|
"""
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
Switches the credential source (between environment and file) and
|
|
285
|
-
re-evaluates the authentication type based on the new credential set.
|
|
286
|
-
This is typically called when the current credential source fails
|
|
287
|
-
or when switching contexts.
|
|
201
|
+
Switch credential source and re-evaluate authentication type.
|
|
288
202
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
None
|
|
292
|
-
|
|
293
|
-
Notes
|
|
294
|
-
-----
|
|
295
|
-
This method extends the parent class behavior by also re-evaluating
|
|
296
|
-
the authentication type, which may change based on different
|
|
297
|
-
credentials available in the new source.
|
|
203
|
+
Changes between environment and file credential sources, then re-evaluates
|
|
204
|
+
authentication type based on the new credentials.
|
|
298
205
|
"""
|
|
299
206
|
super().change_origin()
|
|
300
207
|
|
|
@@ -307,29 +214,12 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
307
214
|
|
|
308
215
|
def set_auth_type(self) -> None:
|
|
309
216
|
"""
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
Analyzes the available credentials and determines the appropriate
|
|
313
|
-
authentication method based on the following priority:
|
|
314
|
-
1. EXCHANGE - Personal access token available
|
|
315
|
-
2. OAUTH2 - Access token and refresh token available
|
|
316
|
-
3. ACCESS_TOKEN - Only access token available
|
|
317
|
-
4. BASIC - Username and password available
|
|
318
|
-
5. None - No valid credentials found
|
|
319
|
-
|
|
320
|
-
For EXCHANGE authentication, automatically performs token exchange
|
|
321
|
-
and switches to file-based credential storage.
|
|
322
|
-
|
|
323
|
-
Returns
|
|
324
|
-
-------
|
|
325
|
-
None
|
|
217
|
+
Determine authentication type from available credentials.
|
|
326
218
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
- Changes origin to file-based storage for the new tokens
|
|
332
|
-
- Updates the authentication type accordingly
|
|
219
|
+
Evaluates credentials in priority order: EXCHANGE (personal access token),
|
|
220
|
+
OAUTH2 (access + refresh tokens), ACCESS_TOKEN (access only), BASIC
|
|
221
|
+
(username + password). For EXCHANGE type, automatically exchanges the
|
|
222
|
+
personal access token and switches to file-based credentials storage.
|
|
333
223
|
"""
|
|
334
224
|
creds = creds_handler.get_credentials(self._origin)
|
|
335
225
|
self._auth_type = self._eval_auth_type(creds)
|
|
@@ -343,55 +233,34 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
343
233
|
|
|
344
234
|
def refreshable_auth_types(self) -> bool:
|
|
345
235
|
"""
|
|
346
|
-
Check if
|
|
236
|
+
Check if current authentication supports token refresh.
|
|
347
237
|
|
|
348
|
-
|
|
349
|
-
|
|
238
|
+
Returns True for OAUTH2 (refresh token) and EXCHANGE (personal access token),
|
|
239
|
+
False for BASIC and ACCESS_TOKEN.
|
|
350
240
|
|
|
351
241
|
Returns
|
|
352
242
|
-------
|
|
353
243
|
bool
|
|
354
|
-
|
|
355
|
-
False otherwise (BASIC or ACCESS_TOKEN).
|
|
356
|
-
|
|
357
|
-
Notes
|
|
358
|
-
-----
|
|
359
|
-
Only OAUTH2 and EXCHANGE authentication types support refresh:
|
|
360
|
-
- OAUTH2: Uses refresh token to get new access tokens
|
|
361
|
-
- EXCHANGE: Uses personal access token for token exchange
|
|
362
|
-
- BASIC and ACCESS_TOKEN do not support refresh
|
|
244
|
+
Whether authentication type supports refresh.
|
|
363
245
|
"""
|
|
364
246
|
return self._auth_type in [AuthType.OAUTH2.value, AuthType.EXCHANGE.value]
|
|
365
247
|
|
|
366
248
|
def get_auth_parameters(self, kwargs: dict) -> dict:
|
|
367
249
|
"""
|
|
368
|
-
Add authentication parameters to HTTP request
|
|
250
|
+
Add authentication headers/parameters to HTTP request kwargs.
|
|
369
251
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
type and available credentials.
|
|
252
|
+
Adds Authorization Bearer header for token-based auth or auth tuple
|
|
253
|
+
for basic authentication.
|
|
373
254
|
|
|
374
255
|
Parameters
|
|
375
256
|
----------
|
|
376
257
|
kwargs : dict
|
|
377
|
-
HTTP request
|
|
258
|
+
HTTP request arguments to modify.
|
|
378
259
|
|
|
379
260
|
Returns
|
|
380
261
|
-------
|
|
381
262
|
dict
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
Notes
|
|
385
|
-
-----
|
|
386
|
-
Authentication is added based on auth type:
|
|
387
|
-
- OAUTH2/EXCHANGE/ACCESS_TOKEN: Adds Authorization Bearer header
|
|
388
|
-
- BASIC: Adds auth tuple with username/password
|
|
389
|
-
- None: No authentication added
|
|
390
|
-
|
|
391
|
-
The method assumes that:
|
|
392
|
-
- Authentication type has been properly set
|
|
393
|
-
- For EXCHANGE type, refresh token has been obtained
|
|
394
|
-
- Required credentials are available for the current auth type
|
|
263
|
+
Modified kwargs with authentication parameters.
|
|
395
264
|
"""
|
|
396
265
|
creds = creds_handler.get_credentials(self._origin)
|
|
397
266
|
if self._auth_type in (
|
|
@@ -411,51 +280,32 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
411
280
|
|
|
412
281
|
def refresh_credentials(self, change_origin: bool = False) -> None:
|
|
413
282
|
"""
|
|
414
|
-
Refresh authentication
|
|
283
|
+
Refresh authentication tokens using OAuth2 flows.
|
|
415
284
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
285
|
+
Uses refresh_token grant for OAUTH2 or token exchange for EXCHANGE authentication.
|
|
286
|
+
On 400/401/403 errors with change_origin=True, attempts to switch credential
|
|
287
|
+
sources and retry. Saves new credentials to configuration file.
|
|
419
288
|
|
|
420
289
|
Parameters
|
|
421
290
|
----------
|
|
422
291
|
change_origin : bool, default False
|
|
423
|
-
Whether to
|
|
424
|
-
If True and refresh fails, attempts to switch credential sources
|
|
425
|
-
and retry once.
|
|
426
|
-
|
|
427
|
-
Returns
|
|
428
|
-
-------
|
|
429
|
-
None
|
|
292
|
+
Whether to switch credential sources on auth failure.
|
|
430
293
|
|
|
431
294
|
Raises
|
|
432
295
|
------
|
|
433
296
|
ClientError
|
|
434
|
-
If
|
|
435
|
-
credentials are missing, or if refresh fails and change_origin
|
|
436
|
-
is False.
|
|
437
|
-
|
|
438
|
-
Notes
|
|
439
|
-
-----
|
|
440
|
-
Refresh behavior by authentication type:
|
|
441
|
-
- OAUTH2: Uses refresh_token grant to get new access/refresh tokens
|
|
442
|
-
- EXCHANGE: Uses token exchange with personal access token
|
|
443
|
-
|
|
444
|
-
If refresh fails with 400/401/403 status and change_origin=True,
|
|
445
|
-
attempts to switch credential sources and retry once.
|
|
446
|
-
|
|
447
|
-
New credentials are automatically saved to the configuration file
|
|
448
|
-
and the origin is switched to file-based storage.
|
|
297
|
+
If auth type doesn't support refresh or credentials missing.
|
|
449
298
|
"""
|
|
450
299
|
if not self.refreshable_auth_types():
|
|
451
300
|
raise ClientError(f"Auth type {self._auth_type} does not support refresh.")
|
|
452
301
|
|
|
453
|
-
# Get refresh endpoint
|
|
454
|
-
url = self._get_refresh_endpoint()
|
|
455
|
-
|
|
456
302
|
# Get credentials
|
|
457
303
|
creds = self._creds_handler.get_credentials(self._origin)
|
|
458
304
|
|
|
305
|
+
# Get token refresh from creds
|
|
306
|
+
if (url := creds.get(CredsEnvVar.OAUTH2_TOKEN_ENDPOINT.value)) is None:
|
|
307
|
+
url = self._get_refresh_endpoint(creds)
|
|
308
|
+
|
|
459
309
|
# Get client id
|
|
460
310
|
if (client_id := creds.get(CredsEnvVar.DHCORE_CLIENT_ID.value)) is None:
|
|
461
311
|
raise ClientError("Client id not set.")
|
|
@@ -491,67 +341,24 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
491
341
|
# Read new credentials and propagate to config file
|
|
492
342
|
self._export_new_creds(response.json())
|
|
493
343
|
|
|
494
|
-
def
|
|
344
|
+
def _get_refresh_endpoint(self, creds: dict) -> str:
|
|
495
345
|
"""
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
Creates a list of credential key names with "DHCORE_" prefix removed
|
|
499
|
-
from specific keys that are stored without the prefix in configuration
|
|
500
|
-
files for compatibility with CLI tools.
|
|
501
|
-
|
|
502
|
-
Returns
|
|
503
|
-
-------
|
|
504
|
-
list[str]
|
|
505
|
-
List of credential keys with selective prefix removal applied.
|
|
506
|
-
|
|
507
|
-
Notes
|
|
508
|
-
-----
|
|
509
|
-
Keys that have prefix removed (defined in keys_to_unprefix):
|
|
510
|
-
- DHCORE_REFRESH_TOKEN -> refresh_token
|
|
511
|
-
- DHCORE_ACCESS_TOKEN -> access_token
|
|
512
|
-
- DHCORE_ISSUER -> issuer
|
|
513
|
-
- DHCORE_CLIENT_ID -> client_id
|
|
514
|
-
|
|
515
|
-
Other keys retain their full names for consistency.
|
|
516
|
-
"""
|
|
517
|
-
new_list = []
|
|
518
|
-
for key in self.keys:
|
|
519
|
-
if key in self.keys_to_unprefix:
|
|
520
|
-
new_list.append(key.removeprefix("DHCORE_"))
|
|
521
|
-
else:
|
|
522
|
-
new_list.append(key)
|
|
523
|
-
return new_list
|
|
346
|
+
Discover OAuth2 token endpoint from issuer well-known configuration.
|
|
524
347
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
Discover the OAuth2 token refresh endpoint from the issuer.
|
|
348
|
+
Queries /.well-known/openid-configuration to extract token_endpoint for
|
|
349
|
+
credential refresh operations.
|
|
528
350
|
|
|
529
|
-
|
|
530
|
-
|
|
351
|
+
Parameters
|
|
352
|
+
----------
|
|
353
|
+
creds : dict
|
|
354
|
+
Available credential values.
|
|
531
355
|
|
|
532
356
|
Returns
|
|
533
357
|
-------
|
|
534
358
|
str
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
Raises
|
|
538
|
-
------
|
|
539
|
-
ClientError
|
|
540
|
-
If the issuer endpoint is not configured.
|
|
541
|
-
HTTPError
|
|
542
|
-
If the well-known configuration endpoint is not accessible.
|
|
543
|
-
KeyError
|
|
544
|
-
If the token_endpoint is not found in the issuer configuration.
|
|
545
|
-
|
|
546
|
-
Notes
|
|
547
|
-
-----
|
|
548
|
-
This method follows the OAuth2/OpenID Connect discovery standard by:
|
|
549
|
-
1. Accessing the issuer's /.well-known/openid-configuration endpoint
|
|
550
|
-
2. Extracting the token_endpoint from the configuration
|
|
551
|
-
3. Using this endpoint for subsequent token refresh operations
|
|
359
|
+
Token endpoint URL for credential refresh.
|
|
552
360
|
"""
|
|
553
361
|
# Get issuer endpoint
|
|
554
|
-
creds = self._creds_handler.get_credentials(self._origin)
|
|
555
362
|
endpoint_issuer = creds.get(CredsEnvVar.DHCORE_ISSUER.value)
|
|
556
363
|
if endpoint_issuer is None:
|
|
557
364
|
raise ClientError("Issuer endpoint not set.")
|
|
@@ -570,30 +377,22 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
570
377
|
**kwargs,
|
|
571
378
|
) -> Response:
|
|
572
379
|
"""
|
|
573
|
-
Make
|
|
380
|
+
Make OAuth2 token refresh request.
|
|
574
381
|
|
|
575
|
-
|
|
576
|
-
|
|
382
|
+
Sends POST request with form-encoded payload using required OAuth2
|
|
383
|
+
content type and 60-second timeout.
|
|
577
384
|
|
|
578
385
|
Parameters
|
|
579
386
|
----------
|
|
580
387
|
url : str
|
|
581
|
-
|
|
388
|
+
Token endpoint URL.
|
|
582
389
|
**kwargs : dict
|
|
583
|
-
Token request parameters
|
|
584
|
-
refresh_token, subject_token, etc.
|
|
390
|
+
Token request parameters (grant_type, client_id, etc.).
|
|
585
391
|
|
|
586
392
|
Returns
|
|
587
393
|
-------
|
|
588
394
|
Response
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
Notes
|
|
592
|
-
-----
|
|
593
|
-
This method:
|
|
594
|
-
- Uses application/x-www-form-urlencoded content type as required by OAuth2
|
|
595
|
-
- Sets a 60-second timeout for the request
|
|
596
|
-
- Returns the raw response for caller to handle status and parsing
|
|
395
|
+
Raw HTTP response for caller handling.
|
|
597
396
|
"""
|
|
598
397
|
# Send request to get new access token
|
|
599
398
|
payload = {**kwargs}
|
|
@@ -602,30 +401,20 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
602
401
|
|
|
603
402
|
def _eval_auth_type(self, creds: dict) -> str | None:
|
|
604
403
|
"""
|
|
605
|
-
|
|
404
|
+
Determine authentication type from available credentials.
|
|
606
405
|
|
|
607
|
-
|
|
608
|
-
|
|
406
|
+
Evaluates in priority order: EXCHANGE (personal access token), OAUTH2
|
|
407
|
+
(access + refresh tokens), ACCESS_TOKEN (access only), BASIC (username + password).
|
|
609
408
|
|
|
610
409
|
Parameters
|
|
611
410
|
----------
|
|
612
411
|
creds : dict
|
|
613
|
-
|
|
412
|
+
Available credential values.
|
|
614
413
|
|
|
615
414
|
Returns
|
|
616
415
|
-------
|
|
617
416
|
str or None
|
|
618
|
-
|
|
619
|
-
if no valid authentication method can be determined.
|
|
620
|
-
|
|
621
|
-
Notes
|
|
622
|
-
-----
|
|
623
|
-
Authentication type priority (checked in order):
|
|
624
|
-
1. EXCHANGE - Personal access token is available
|
|
625
|
-
2. OAUTH2 - Both access token and refresh token are available
|
|
626
|
-
3. ACCESS_TOKEN - Only access token is available
|
|
627
|
-
4. BASIC - Both username and password are available
|
|
628
|
-
5. None - No valid credential combination found
|
|
417
|
+
Authentication type from AuthType enum, or None if no valid credentials.
|
|
629
418
|
"""
|
|
630
419
|
if creds[CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value] is not None:
|
|
631
420
|
return AuthType.EXCHANGE.value
|
|
@@ -642,32 +431,24 @@ class ClientDHCoreConfigurator(Configurator):
|
|
|
642
431
|
|
|
643
432
|
def _export_new_creds(self, response: dict) -> None:
|
|
644
433
|
"""
|
|
645
|
-
Save
|
|
434
|
+
Save refreshed credentials and switch to file-based storage.
|
|
646
435
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
reloads file-based credentials and switches to file origin.
|
|
436
|
+
Persists new tokens (access_token, refresh_token, etc.) to configuration
|
|
437
|
+
file and switches credential origin to file storage.
|
|
650
438
|
|
|
651
439
|
Parameters
|
|
652
440
|
----------
|
|
653
441
|
response : dict
|
|
654
|
-
|
|
655
|
-
and other credential information.
|
|
656
|
-
|
|
657
|
-
Returns
|
|
658
|
-
-------
|
|
659
|
-
None
|
|
660
|
-
|
|
661
|
-
Notes
|
|
662
|
-
-----
|
|
663
|
-
This method:
|
|
664
|
-
1. Writes new credentials to the configuration file
|
|
665
|
-
2. Reloads file-based credentials to ensure consistency
|
|
666
|
-
3. Changes current origin to file since new tokens are file-based
|
|
667
|
-
|
|
668
|
-
The response typically contains access_token, refresh_token,
|
|
669
|
-
token_type, expires_in, and other OAuth2 standard fields.
|
|
442
|
+
OAuth2 token response with new credentials.
|
|
670
443
|
"""
|
|
444
|
+
for key in self.keys_to_prefix:
|
|
445
|
+
if key == CredsEnvVar.OAUTH2_TOKEN_ENDPOINT.value:
|
|
446
|
+
prefix = "oauth2_"
|
|
447
|
+
else:
|
|
448
|
+
prefix = "dhcore_"
|
|
449
|
+
key = key.lower()
|
|
450
|
+
if key.removeprefix(prefix) in response:
|
|
451
|
+
response[key] = response.pop(key.removeprefix(prefix))
|
|
671
452
|
creds_handler.write_env(response)
|
|
672
453
|
self.load_file_vars()
|
|
673
454
|
|