digitalhub 0.12.0__py3-none-any.whl → 0.13.0b0__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.

Files changed (36) hide show
  1. digitalhub/__init__.py +1 -1
  2. digitalhub/entities/_base/executable/entity.py +7 -0
  3. digitalhub/entities/_base/material/entity.py +8 -15
  4. digitalhub/entities/_base/material/utils.py +1 -1
  5. digitalhub/entities/_processors/context.py +1 -1
  6. digitalhub/stores/client/dhcore/client.py +56 -19
  7. digitalhub/stores/client/dhcore/configurator.py +278 -183
  8. digitalhub/stores/client/dhcore/enums.py +2 -0
  9. digitalhub/stores/client/dhcore/utils.py +8 -8
  10. digitalhub/stores/{configurator → credentials}/api.py +3 -3
  11. digitalhub/stores/credentials/configurator.py +37 -0
  12. digitalhub/stores/credentials/enums.py +54 -0
  13. digitalhub/stores/{configurator/configurator.py → credentials/handler.py} +23 -77
  14. digitalhub/stores/{configurator → credentials}/ini_module.py +1 -1
  15. digitalhub/stores/credentials/store.py +49 -0
  16. digitalhub/stores/data/_base/store.py +19 -6
  17. digitalhub/stores/data/api.py +37 -1
  18. digitalhub/stores/data/builder.py +46 -53
  19. digitalhub/stores/data/local/store.py +4 -7
  20. digitalhub/stores/data/remote/store.py +4 -7
  21. digitalhub/stores/data/s3/configurator.py +36 -92
  22. digitalhub/stores/data/s3/store.py +42 -57
  23. digitalhub/stores/data/s3/utils.py +1 -1
  24. digitalhub/stores/data/sql/configurator.py +35 -83
  25. digitalhub/stores/data/sql/store.py +15 -15
  26. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/METADATA +1 -1
  27. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/RECORD +31 -33
  28. digitalhub/stores/configurator/credentials_store.py +0 -69
  29. digitalhub/stores/configurator/enums.py +0 -25
  30. digitalhub/stores/data/s3/enums.py +0 -20
  31. digitalhub/stores/data/sql/enums.py +0 -20
  32. digitalhub/stores/data/utils.py +0 -38
  33. /digitalhub/stores/{configurator → credentials}/__init__.py +0 -0
  34. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/WHEEL +0 -0
  35. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/licenses/AUTHORS +0 -0
  36. {digitalhub-0.12.0.dist-info → digitalhub-0.13.0b0.dist-info}/licenses/LICENSE +0 -0
@@ -5,14 +5,13 @@
5
5
  from __future__ import annotations
6
6
 
7
7
  import typing
8
- from warnings import warn
9
8
 
10
9
  from requests import request
11
10
 
12
- from digitalhub.stores.client.dhcore.enums import AuthType, DhcoreEnvVar
13
- from digitalhub.stores.configurator.configurator import configurator
14
- from digitalhub.stores.data.s3.enums import S3StoreEnv
15
- from digitalhub.stores.data.sql.enums import SqlStoreEnv
11
+ from digitalhub.stores.client.dhcore.enums import AuthType
12
+ from digitalhub.stores.credentials.configurator import Configurator
13
+ from digitalhub.stores.credentials.enums import CredsEnvVar, CredsOrigin
14
+ from digitalhub.stores.credentials.handler import creds_handler
16
15
  from digitalhub.utils.exceptions import ClientError
17
16
  from digitalhub.utils.generic_utils import list_enum
18
17
  from digitalhub.utils.uri_utils import has_remote_scheme
@@ -21,191 +20,282 @@ if typing.TYPE_CHECKING:
21
20
  from requests import Response
22
21
 
23
22
 
24
- # Default key used to store authentication information
25
- AUTH_KEY = "_auth"
26
-
27
- # API levels that are supported
28
- MAX_API_LEVEL = 20
29
- MIN_API_LEVEL = 11
30
- LIB_VERSION = 11
31
-
32
-
33
- class ClientDHCoreConfigurator:
23
+ class ClientDHCoreConfigurator(Configurator):
34
24
  """
35
25
  Configurator object used to configure the client.
26
+
27
+ The configurator starts reading the credentials from the
28
+ environment and from the ini file and stores them into the
29
+ creds_handler object.
30
+
31
+ While reading the credentials from the two sources (environment and file),
32
+ the configurator evaluate if the required keys are present in both sources.
33
+ If the required keys are not present in both sources, the configurator
34
+ will rise an error, otherwise decide which source to use.
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.
36
52
  """
37
53
 
54
+ keys = [*list_enum(CredsEnvVar)]
55
+ required_keys = [CredsEnvVar.DHCORE_ENDPOINT.value]
56
+ keys_to_unprefix = [
57
+ CredsEnvVar.DHCORE_REFRESH_TOKEN.value,
58
+ CredsEnvVar.DHCORE_ACCESS_TOKEN.value,
59
+ CredsEnvVar.DHCORE_ISSUER.value,
60
+ CredsEnvVar.DHCORE_CLIENT_ID.value,
61
+ ]
62
+
38
63
  def __init__(self) -> None:
39
- self._current_env = configurator.get_current_env()
64
+ super().__init__()
65
+ self.load_configs()
66
+ self._origin = self.set_origin()
67
+ self._current_profile = creds_handler.get_current_env()
68
+ self._auth_type: str | None = None
69
+ self.set_auth_type()
40
70
 
41
71
  ##############################
42
- # Configuration methods
72
+ # Credentials methods
43
73
  ##############################
44
74
 
45
- def check_config(self) -> None:
75
+ def load_configs(self) -> str:
46
76
  """
47
- Check if the config is valid.
48
-
49
- Parameters
50
- ----------
51
- config : dict
52
- Configuration dictionary.
77
+ Load the configuration from the environment and from the file.
78
+ """
79
+ self.load_env_vars()
80
+ self.load_file_vars()
53
81
 
54
- Returns
55
- -------
56
- None
82
+ def load_env_vars(self) -> None:
57
83
  """
58
- if configurator.get_current_env() != self._current_env:
59
- self.configure()
84
+ Load the credentials from the environment.
85
+ """
86
+ env_creds = {var: self._creds_handler.load_from_env(var) for var in self.keys}
87
+ env_creds = self._sanitize_env_vars(env_creds)
88
+ self._creds_handler.set_credentials(self._env, env_creds)
60
89
 
61
- def configure(self, config: dict | None = None) -> None:
90
+ def _sanitize_env_vars(self, creds: dict) -> dict:
62
91
  """
63
- Configure the client attributes with config (given or from
64
- environment).
65
- Regarding authentication parameters, the config parameter
66
- takes precedence over the env variables, and the token
67
- over the basic auth. Furthermore, the config parameter is
68
- validated against the proper pydantic model.
92
+ Sanitize the env vars. We expect issuer to have the
93
+ form "DHCORE_ISSUER" in env.
69
94
 
70
95
  Parameters
71
96
  ----------
72
- config : dict
73
- Configuration dictionary.
97
+ creds : dict
98
+ Credentials dictionary.
74
99
 
75
100
  Returns
76
101
  -------
77
- None
102
+ dict
78
103
  """
79
- self._get_core_endpoint()
80
- self._get_auth_vars()
104
+ creds[CredsEnvVar.DHCORE_ENDPOINT.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ENDPOINT.value])
105
+ creds[CredsEnvVar.DHCORE_ISSUER.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ISSUER.value])
106
+ return creds
81
107
 
82
- def check_core_version(self, response: Response) -> None:
108
+ def load_file_vars(self) -> None:
109
+ """
110
+ Load the credentials from the file.
83
111
  """
84
- Raise an exception if DHCore API version is not supported.
112
+ keys = [*self._remove_prefix_dhcore()]
113
+ file_creds = {var: self._creds_handler.load_from_file(var) for var in keys}
85
114
 
86
- Parameters
87
- ----------
88
- response : Response
89
- The response object.
115
+ # Because in the response there is no endpoint
116
+ if file_creds[CredsEnvVar.DHCORE_ENDPOINT.value] is None:
117
+ file_creds[CredsEnvVar.DHCORE_ENDPOINT.value] = self._creds_handler.load_from_env(
118
+ CredsEnvVar.DHCORE_ENDPOINT.value
119
+ )
90
120
 
91
- Returns
92
- -------
93
- None
94
- """
95
- if "X-Api-Level" in response.headers:
96
- core_api_level = int(response.headers["X-Api-Level"])
97
- if not (MIN_API_LEVEL <= core_api_level <= MAX_API_LEVEL):
98
- raise ClientError("Backend API level not supported.")
99
- if LIB_VERSION < core_api_level:
100
- warn("Backend API level is higher than library version. You should consider updating the library.")
121
+ # Because in the response there is no personal access token
122
+ if file_creds[CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value] is None:
123
+ file_creds[CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value] = self._creds_handler.load_from_env(
124
+ CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value
125
+ )
126
+
127
+ file_creds = self._sanitize_file_vars(file_creds)
128
+ self._creds_handler.set_credentials(self._file, file_creds)
101
129
 
102
- def build_url(self, api: str) -> str:
130
+ def _sanitize_file_vars(self, creds: dict) -> dict:
103
131
  """
104
- Build the url.
132
+ Sanitize the file vars. We expect issuer, client_id and access_token and
133
+ refresh_token to not have the form "DHCORE_" in the file.
105
134
 
106
135
  Parameters
107
136
  ----------
108
- api : str
109
- The api to call.
137
+ creds : dict
138
+ Credentials dictionary.
110
139
 
111
140
  Returns
112
141
  -------
113
- str
114
- The url.
142
+ dict
115
143
  """
116
- api = api.removeprefix("/")
117
- return f"{configurator.get_credential(DhcoreEnvVar.ENDPOINT.value)}/{api}"
118
-
119
- ##############################
120
- # Private methods
121
- ##############################
144
+ creds[CredsEnvVar.DHCORE_ENDPOINT.value] = self._sanitize_endpoint(creds[CredsEnvVar.DHCORE_ENDPOINT.value])
145
+ creds[CredsEnvVar.DHCORE_ISSUER.value] = self._sanitize_endpoint(
146
+ creds[CredsEnvVar.DHCORE_ISSUER.value.removeprefix("DHCORE_")]
147
+ )
148
+ creds[CredsEnvVar.DHCORE_REFRESH_TOKEN.value] = creds[
149
+ CredsEnvVar.DHCORE_REFRESH_TOKEN.value.removeprefix("DHCORE_")
150
+ ]
151
+ creds[CredsEnvVar.DHCORE_ACCESS_TOKEN.value] = creds[
152
+ CredsEnvVar.DHCORE_ACCESS_TOKEN.value.removeprefix("DHCORE_")
153
+ ]
154
+ creds[CredsEnvVar.DHCORE_CLIENT_ID.value] = creds[CredsEnvVar.DHCORE_CLIENT_ID.value.removeprefix("DHCORE_")]
155
+ return {k: v for k, v in creds.items() if k in self.keys}
122
156
 
123
157
  @staticmethod
124
- def _sanitize_endpoint(endpoint: str) -> str:
158
+ def _sanitize_endpoint(endpoint: str | None = None) -> str | None:
125
159
  """
126
160
  Sanitize the endpoint.
127
161
 
128
162
  Returns
129
163
  -------
130
- None
164
+ str | None
165
+ The sanitized endpoint.
131
166
  """
167
+ if endpoint is None:
168
+ return
132
169
  if not has_remote_scheme(endpoint):
133
170
  raise ClientError("Invalid endpoint scheme. Must start with http:// or https://.")
134
171
 
135
172
  endpoint = endpoint.strip()
136
173
  return endpoint.removesuffix("/")
137
174
 
138
- def _get_core_endpoint(self) -> None:
175
+ def check_config(self) -> None:
139
176
  """
140
- Get the DHCore endpoint from env.
177
+ Check if the config is valid.
178
+
179
+ Parameters
180
+ ----------
181
+ config : dict
182
+ Configuration dictionary.
141
183
 
142
184
  Returns
143
185
  -------
144
186
  None
187
+ """
188
+ if (current := creds_handler.get_current_env()) != self._current_profile:
189
+ self.load_file_vars()
190
+ self._current_profile = current
145
191
 
146
- Raises
147
- ------
148
- Exception
149
- If the endpoint of DHCore is not set in the env variables.
192
+ def get_endpoint(self) -> str:
150
193
  """
151
- endpoint = configurator.load_var(DhcoreEnvVar.ENDPOINT.value)
152
- if endpoint is None:
153
- raise ClientError("Endpoint not set as environment variables.")
154
- endpoint = self._sanitize_endpoint(endpoint)
155
- configurator.set_credential(DhcoreEnvVar.ENDPOINT.value, endpoint)
194
+ Get the DHCore endpoint.
156
195
 
157
- def _get_auth_vars(self) -> None:
196
+ Returns
197
+ -------
198
+ str
199
+ The endpoint.
158
200
  """
159
- Get authentication parameters from the env.
201
+ creds = self._creds_handler.get_credentials(self._origin)
202
+ return creds[CredsEnvVar.DHCORE_ENDPOINT.value]
203
+
204
+ ##############################
205
+ # Origin methods
206
+ ##############################
207
+
208
+ def set_origin(self) -> str:
209
+ """
210
+ Evaluate the default origin from the credentials.
160
211
 
161
212
  Returns
162
213
  -------
163
- None
214
+ str
215
+ The origin.
164
216
  """
165
- # Give priority to access token
166
- access_token = self._load_dhcore_oauth_vars(DhcoreEnvVar.ACCESS_TOKEN.value)
167
- if access_token is not None:
168
- configurator.set_credential(AUTH_KEY, AuthType.OAUTH2.value)
169
- configurator.set_credential(DhcoreEnvVar.ACCESS_TOKEN.value.removeprefix("DHCORE_"), access_token)
217
+ origin = CredsOrigin.ENV.value
170
218
 
171
- # Fallback to basic
219
+ env_creds = self._creds_handler.get_credentials(self._env)
220
+ missing_env = self._check_credentials(env_creds)
221
+
222
+ file_creds = self._creds_handler.get_credentials(self._file)
223
+ missing_file = self._check_credentials(file_creds)
224
+
225
+ msg = ""
226
+ if missing_env:
227
+ msg = f"Missing required vars in env: {', '.join(missing_env)}"
228
+ origin = CredsOrigin.FILE.value
229
+ elif missing_file:
230
+ msg += f"Missing required vars in .dhcore.ini file: {', '.join(missing_file)}"
231
+
232
+ if missing_env and missing_file:
233
+ raise ClientError(msg)
234
+
235
+ return origin
236
+
237
+ def change_origin(self) -> None:
238
+ """
239
+ Change the origin of the credentials.
240
+ """
241
+ if self._origin == CredsOrigin.ENV.value:
242
+ self.change_to_file()
172
243
  else:
173
- user = configurator.load_var(DhcoreEnvVar.USER.value)
174
- password = configurator.load_var(DhcoreEnvVar.PASSWORD.value)
175
- if user is not None and password is not None:
176
- configurator.set_credential(AUTH_KEY, AuthType.BASIC.value)
177
- configurator.set_credential(DhcoreEnvVar.USER.value, user)
178
- configurator.set_credential(DhcoreEnvVar.PASSWORD.value, password)
244
+ self.change_to_env()
245
+
246
+ # Re-evaluate the auth type
247
+ self.set_auth_type()
248
+
249
+ def change_to_file(self) -> None:
250
+ """
251
+ Change the origin to file. Re-evaluate the auth type.
252
+ """
253
+ self._origin = CredsOrigin.FILE.value
254
+
255
+ def change_to_env(self) -> None:
256
+ """
257
+ Change the origin to env. Re-evaluate the auth type.
258
+ """
259
+ self._origin = CredsOrigin.ENV.value
179
260
 
180
261
  ##############################
181
262
  # Auth methods
182
263
  ##############################
183
264
 
184
- def basic_auth(self) -> bool:
265
+ def set_auth_type(self) -> None:
185
266
  """
186
- Get basic auth.
267
+ Evaluate the auth type from the credentials.
187
268
 
188
269
  Returns
189
270
  -------
190
- bool
271
+ None
191
272
  """
192
- auth_type = configurator.get_credential(AUTH_KEY)
193
- return auth_type == AuthType.BASIC.value
273
+ creds = creds_handler.get_credentials(self._origin)
274
+ self._auth_type = self._eval_auth_type(creds)
275
+ # If we have an exchange token, we need to get a new access token.
276
+ # Therefore, we change the origin to file, where the refresh token is written.
277
+ # We also try to fetch the PAT from both env and file
278
+ if self._auth_type == AuthType.EXCHANGE.value:
279
+ self.get_new_access_token(change_origin=True)
280
+ # Just to ensure we get the right source from file
281
+ self.change_to_file()
194
282
 
195
- def oauth2_auth(self) -> bool:
283
+ def refreshable_auth_types(self) -> bool:
196
284
  """
197
- Get oauth2 auth.
285
+ Check if the auth type is refreshable.
198
286
 
199
287
  Returns
200
288
  -------
201
289
  bool
290
+ True if the auth type is refreshable, False otherwise.
202
291
  """
203
- auth_type = configurator.get_credential(AUTH_KEY)
204
- return auth_type == AuthType.OAUTH2.value
292
+ return self._auth_type in [AuthType.OAUTH2.value, AuthType.EXCHANGE.value]
205
293
 
206
- def set_request_auth(self, kwargs: dict) -> dict:
294
+ def get_auth_parameters(self, kwargs: dict) -> dict:
207
295
  """
208
- Get the authentication header.
296
+ Get the authentication header for the request.
297
+ It is given for granted that the auth type is set and that,
298
+ if the auth type is EXCHANGE, the refresh token is set.
209
299
 
210
300
  Parameters
211
301
  ----------
@@ -215,50 +305,78 @@ class ClientDHCoreConfigurator:
215
305
  Returns
216
306
  -------
217
307
  dict
218
- Authentication header.
219
- """
220
- creds = configurator.get_all_credentials()
221
- if AUTH_KEY not in creds:
222
- return kwargs
223
- if self.basic_auth():
224
- user = creds[DhcoreEnvVar.USER.value]
225
- password = creds[DhcoreEnvVar.PASSWORD.value]
226
- kwargs["auth"] = (user, password)
227
- elif self.oauth2_auth():
308
+ Authentication parameters.
309
+ """
310
+ creds = creds_handler.get_credentials(self._origin)
311
+ if self._auth_type in (AuthType.EXCHANGE.value, AuthType.OAUTH2.value):
312
+ access_token = creds[CredsEnvVar.DHCORE_ACCESS_TOKEN.value]
228
313
  if "headers" not in kwargs:
229
314
  kwargs["headers"] = {}
230
- access_token = creds[DhcoreEnvVar.ACCESS_TOKEN.value.removeprefix("DHCORE_")]
231
315
  kwargs["headers"]["Authorization"] = f"Bearer {access_token}"
316
+ elif self._auth_type == AuthType.BASIC.value:
317
+ user = creds[CredsEnvVar.DHCORE_USER.value]
318
+ password = creds[CredsEnvVar.DHCORE_PASSWORD.value]
319
+ kwargs["auth"] = (user, password)
232
320
  return kwargs
233
321
 
234
- def get_new_access_token(self) -> None:
322
+ def get_new_access_token(self, change_origin: bool = False) -> None:
235
323
  """
236
324
  Get a new access token.
237
325
 
326
+ Parameters
327
+ ----------
328
+ change_origin : bool, optional
329
+ Whether to change the origin of the credentials, by default False
330
+
238
331
  Returns
239
332
  -------
240
333
  None
241
334
  """
242
- # Call issuer and get endpoint for
243
- # refreshing access token
335
+ if not self.refreshable_auth_types():
336
+ raise ClientError(f"Auth type {self._auth_type} does not support refresh.")
337
+
338
+ # Get refresh endpoint
244
339
  url = self._get_refresh_endpoint()
245
340
 
246
- # Call refresh token endpoint
247
- # Try token from env
248
- refresh_token = configurator.load_from_env(DhcoreEnvVar.REFRESH_TOKEN.value)
249
- response = self._call_refresh_token_endpoint(url, refresh_token)
341
+ # Get credentials
342
+ creds = self._creds_handler.get_credentials(self._origin)
343
+
344
+ # Get client id
345
+ if (client_id := creds.get(CredsEnvVar.DHCORE_CLIENT_ID.value)) is None:
346
+ raise ClientError("Client id not set.")
250
347
 
251
- # Otherwise try token from file
348
+ # Handling of token exchange or refresh
349
+ if self._auth_type == AuthType.OAUTH2.value:
350
+ response = self._call_refresh_token_endpoint(
351
+ url,
352
+ client_id=client_id,
353
+ refresh_token=creds.get(CredsEnvVar.DHCORE_REFRESH_TOKEN.value),
354
+ grant_type="refresh_token",
355
+ scope="credentials",
356
+ )
357
+ elif self._auth_type == AuthType.EXCHANGE.value:
358
+ response = self._call_refresh_token_endpoint(
359
+ url,
360
+ client_id=client_id,
361
+ subject_token=creds.get(CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value),
362
+ subject_token_type="urn:ietf:params:oauth:token-type:pat",
363
+ grant_type="urn:ietf:params:oauth:grant-type:token-exchange",
364
+ scope="credentials",
365
+ )
366
+
367
+ # Change origin of creds if needed
252
368
  if response.status_code in (400, 401, 403):
253
- refresh_token = configurator.load_from_file(DhcoreEnvVar.REFRESH_TOKEN.value.removeprefix("DHCORE_"))
254
- response = self._call_refresh_token_endpoint(url, refresh_token)
369
+ if not change_origin:
370
+ raise ClientError("Unable to refresh token. Please check your credentials.")
371
+ self.change_origin()
372
+ self.get_new_access_token(change_origin=False)
255
373
 
256
374
  response.raise_for_status()
257
375
 
258
376
  # Read new credentials and propagate to config file
259
- self._set_creds(response.json())
377
+ self._export_new_creds(response.json())
260
378
 
261
- def _set_creds(self, response: dict) -> None:
379
+ def _export_new_creds(self, response: dict) -> None:
262
380
  """
263
381
  Set new credentials.
264
382
 
@@ -271,17 +389,13 @@ class ClientDHCoreConfigurator:
271
389
  -------
272
390
  None
273
391
  """
274
- keys = [
275
- *self._remove_prefix_dhcore(list_enum(DhcoreEnvVar)),
276
- *list_enum(S3StoreEnv),
277
- *list_enum(SqlStoreEnv),
278
- ]
279
- for key in keys:
280
- if (value := response.get(key.lower())) is not None:
281
- configurator.set_credential(key, value)
282
- configurator.write_env(keys)
392
+ creds_handler.write_env(response)
393
+ self.load_file_vars()
283
394
 
284
- def _remove_prefix_dhcore(self, keys: list[str]) -> list[str]:
395
+ # Change current origin to file because of refresh
396
+ self._origin = CredsOrigin.FILE.value
397
+
398
+ def _remove_prefix_dhcore(self) -> list[str]:
285
399
  """
286
400
  Remove prefix from selected keys. (Compatibility with CLI)
287
401
 
@@ -296,13 +410,8 @@ class ClientDHCoreConfigurator:
296
410
  List of keys without prefix.
297
411
  """
298
412
  new_list = []
299
- for key in keys:
300
- if key in (
301
- DhcoreEnvVar.REFRESH_TOKEN.value,
302
- DhcoreEnvVar.ACCESS_TOKEN.value,
303
- DhcoreEnvVar.ISSUER.value,
304
- DhcoreEnvVar.CLIENT_ID.value,
305
- ):
413
+ for key in self.keys:
414
+ if key in self.keys_to_unprefix:
306
415
  new_list.append(key.removeprefix("DHCORE_"))
307
416
  else:
308
417
  new_list.append(key)
@@ -318,11 +427,10 @@ class ClientDHCoreConfigurator:
318
427
  Refresh endpoint.
319
428
  """
320
429
  # Get issuer endpoint
321
- endpoint_issuer = self._load_dhcore_oauth_vars(DhcoreEnvVar.ISSUER.value)
430
+ creds = self._creds_handler.get_credentials(self._origin)
431
+ endpoint_issuer = creds.get(CredsEnvVar.DHCORE_ISSUER.value)
322
432
  if endpoint_issuer is None:
323
433
  raise ClientError("Issuer endpoint not set.")
324
- endpoint_issuer = self._sanitize_endpoint(endpoint_issuer)
325
- configurator.set_credential(DhcoreEnvVar.ISSUER.value.removeprefix("DHCORE_"), endpoint_issuer)
326
434
 
327
435
  # Standard issuer endpoint path
328
436
  url = endpoint_issuer + "/.well-known/openid-configuration"
@@ -332,7 +440,11 @@ class ClientDHCoreConfigurator:
332
440
  r.raise_for_status()
333
441
  return r.json().get("token_endpoint")
334
442
 
335
- def _call_refresh_token_endpoint(self, url: str, refresh_token: str) -> Response:
443
+ def _call_refresh_token_endpoint(
444
+ self,
445
+ url: str,
446
+ **kwargs,
447
+ ) -> Response:
336
448
  """
337
449
  Call the refresh token endpoint.
338
450
 
@@ -340,44 +452,27 @@ class ClientDHCoreConfigurator:
340
452
  ----------
341
453
  url : str
342
454
  Refresh token endpoint.
343
- refresh_token : str
344
- Refresh token.
455
+ kwargs : dict
456
+ Keyword arguments to pass to the request.
345
457
 
346
458
  Returns
347
459
  -------
348
460
  Response
349
461
  Response object.
350
462
  """
351
- # Get client id
352
- client_id = self._load_dhcore_oauth_vars(DhcoreEnvVar.CLIENT_ID.value)
353
- if client_id is None:
354
- raise ClientError("Client id not set.")
355
-
356
463
  # Send request to get new access token
357
- payload = {
358
- "grant_type": "refresh_token",
359
- "client_id": client_id,
360
- "refresh_token": refresh_token,
361
- "scope": "openid credentials offline_access",
362
- }
464
+ payload = {**kwargs}
363
465
  headers = {"Content-Type": "application/x-www-form-urlencoded"}
364
466
  return request("POST", url, data=payload, headers=headers, timeout=60)
365
467
 
366
- def _load_dhcore_oauth_vars(self, oauth_var: str) -> str | None:
367
- """
368
- Load DHCore oauth variables.
369
-
370
- Parameters
371
- ----------
372
- oauth_var : str
373
- The oauth variable to load.
374
-
375
- Returns
376
- -------
377
- str
378
- The oauth variable.
379
- """
380
- read_var = configurator.load_from_env(oauth_var)
381
- if read_var is None:
382
- read_var = configurator.load_from_file(oauth_var.removeprefix("DHCORE_"))
383
- return read_var
468
+ def _eval_auth_type(self, creds: dict) -> str | None:
469
+ if creds[CredsEnvVar.DHCORE_PERSONAL_ACCESS_TOKEN.value] is not None:
470
+ return AuthType.EXCHANGE.value
471
+ if (
472
+ creds[CredsEnvVar.DHCORE_ACCESS_TOKEN.value] is not None
473
+ and creds[CredsEnvVar.DHCORE_REFRESH_TOKEN.value] is not None
474
+ ):
475
+ return AuthType.OAUTH2.value
476
+ if creds[CredsEnvVar.DHCORE_USER.value] is not None and creds[CredsEnvVar.DHCORE_PASSWORD.value] is not None:
477
+ return AuthType.BASIC.value
478
+ return None
@@ -19,6 +19,7 @@ class DhcoreEnvVar(Enum):
19
19
  CLIENT_ID = "DHCORE_CLIENT_ID"
20
20
  ACCESS_TOKEN = "DHCORE_ACCESS_TOKEN"
21
21
  REFRESH_TOKEN = "DHCORE_REFRESH_TOKEN"
22
+ PERSONAL_ACCESS_TOKEN = "DHCORE_PERSONAL_ACCESS_TOKEN"
22
23
  WORKFLOW_IMAGE = "DHCORE_WORKFLOW_IMAGE"
23
24
 
24
25
 
@@ -29,3 +30,4 @@ class AuthType(Enum):
29
30
 
30
31
  BASIC = "basic"
31
32
  OAUTH2 = "oauth2"
33
+ EXCHANGE = "exchange"