dnastack-client-library 3.1.205__py3-none-any.whl → 3.1.208__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.
dnastack/constants.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import os
2
2
 
3
- __version__ = "3.1.205"
3
+ __version__ = "3.1.208"
4
4
 
5
5
  LOCAL_STORAGE_DIRECTORY = os.path.join(os.path.expanduser("~"), '.dnastack')
@@ -1,10 +1,11 @@
1
1
  from typing import List, Any, Dict, Optional
2
+ import logging
2
3
 
3
4
  from dnastack.client.models import ServiceEndpoint
4
5
  from dnastack.common.model_mixin import JsonModelMixin
5
6
  from dnastack.http.authenticators.abstract import Authenticator
6
7
  from dnastack.http.authenticators.oauth2 import OAuth2Authenticator
7
- from dnastack.http.authenticators.oauth2_adapter.models import OAuth2Authentication
8
+ from dnastack.http.authenticators.oauth2_adapter.models import OAuth2Authentication, SUPPORTED_GRANT_TYPES
8
9
 
9
10
 
10
11
  class UnsupportedAuthenticationInformationError(RuntimeError):
@@ -43,10 +44,21 @@ class HttpAuthenticatorFactory:
43
44
 
44
45
  @staticmethod
45
46
  def get_unique_auth_info_list(endpoints: List[ServiceEndpoint]) -> List[Dict[str, Any]]:
47
+ logger = logging.getLogger(__name__)
46
48
  unique_auth_info_map: Dict[str, Dict[str, Any]] = {}
47
49
 
48
50
  for endpoint in endpoints:
49
51
  for auth_info in endpoint.get_authentications():
52
+ grant_type = auth_info.get('grant_type')
53
+
54
+ # Skip authentication methods with unsupported grant types
55
+ if grant_type and grant_type not in SUPPORTED_GRANT_TYPES:
56
+ logger.debug("Skipping authentication with unsupported grant type '{}' for resource '{}'".format(
57
+ grant_type,
58
+ auth_info.get('resource_url', 'unknown')
59
+ ))
60
+ continue
61
+
50
62
  unique_auth_info_map[JsonModelMixin.hash(auth_info)] = auth_info
51
63
 
52
64
  return sorted(unique_auth_info_map.values(), key=lambda a: a.get('resource_url') or a.get('type'))
@@ -130,7 +130,7 @@ class OAuth2Authenticator(Authenticator):
130
130
  self.events.dispatch('authentication-failure', event_details)
131
131
 
132
132
  raise OAuth2MisconfigurationError('Cannot determine the type of authentication '
133
- f'({auth_info.json(sort_keys=True, exclude_none=True)})')
133
+ f'({auth_info.model_dump_json(exclude_none=True)})')
134
134
 
135
135
  adapter.check_config_readiness()
136
136
  for auth_event_type in ['blocking-response-required', 'blocking-response-ok', 'blocking-response-failed']:
@@ -255,16 +255,12 @@ class OAuth2Authenticator(Authenticator):
255
255
  except JSONDecodeError:
256
256
  error_msg = refresh_token_res.text
257
257
 
258
- # Handle Wallet-specific implementation
259
- reauthentication_required = (
260
- refresh_token_res.status_code == 401
261
- or
262
- (refresh_token_res.status_code == 400 and 'JWT expired' in error_msg)
263
- )
264
-
265
- if not reauthentication_required:
266
- event_details['reason'] = 'Invalid state while refreshing tokens'
267
- self.events.dispatch('refresh-failure', event_details)
258
+ # Handle token refresh failure based on HTTP status.
259
+ # 400/401 = the refresh token is invalid (expired, revoked, key rotated, etc.)
260
+ # -> prompt the user to re-authenticate
261
+ # 5xx = transient server error, re-login won't help
262
+ # -> raise InvalidStateError
263
+ reauthentication_required = refresh_token_res.status_code in (400, 401)
268
264
 
269
265
  exception_details = {
270
266
  'debug_mode': currently_in_debug_mode(),
@@ -285,8 +281,12 @@ class OAuth2Authenticator(Authenticator):
285
281
  }
286
282
 
287
283
  if reauthentication_required:
288
- raise ReauthenticationRequired('Refresh token expired')
284
+ logger.warning(f'Refresh token rejected by server (HTTP {refresh_token_res.status_code}): {error_msg}')
285
+ raise ReauthenticationRequired(f'Refresh token rejected: {error_msg}')
289
286
  else:
287
+ event_details['reason'] = 'Invalid state while refreshing tokens'
288
+ self.events.dispatch('refresh-failure', event_details)
289
+
290
290
  raise InvalidStateError('Unable to refresh the access token',
291
291
  details=exception_details)
292
292
  finally:
@@ -6,6 +6,15 @@ from dnastack.common.model_mixin import JsonModelMixin as HashableModel
6
6
 
7
7
 
8
8
  GRANT_TYPE_TOKEN_EXCHANGE = 'urn:ietf:params:oauth:grant-type:token-exchange'
9
+ GRANT_TYPE_DEVICE_CODE = 'urn:ietf:params:oauth:grant-type:device_code'
10
+ GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials'
11
+
12
+ # List of grant types supported by the CLI
13
+ SUPPORTED_GRANT_TYPES = [
14
+ GRANT_TYPE_DEVICE_CODE,
15
+ GRANT_TYPE_CLIENT_CREDENTIALS,
16
+ GRANT_TYPE_TOKEN_EXCHANGE,
17
+ ]
9
18
 
10
19
 
11
20
  class OAuth2Authentication(BaseModel, HashableModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dnastack-client-library
3
- Version: 3.1.205
3
+ Version: 3.1.208
4
4
  Summary: DNAstack's GA4GH library and CLI
5
5
  Author-email: DNAstack <devs@dnastack.com>
6
6
  License: Apache License, Version 2.0
@@ -1,6 +1,6 @@
1
1
  dnastack/__init__.py,sha256=mslf7se8vBSK_HkqWTGPdibeVhT4xyKXgzQBV7dEK1M,333
2
2
  dnastack/__main__.py,sha256=EKmtIs4TBseQJi-OT_U6LqRyKLiyrGTBuTQg9zE-G2I,4376
3
- dnastack/constants.py,sha256=AUWbWRGqG9cdfi5lRRv-aBoiIeEYaeFJhbiafQ4bq-o,113
3
+ dnastack/constants.py,sha256=vo13riOSgV4v11y-Mf0p_xg3AUpi_3kAP2ZnW4t_TBY,113
4
4
  dnastack/feature_flags.py,sha256=UMNDB07R_ep6Fx3iClhzwOpkWfyYnb418FpoJo50CGs,1411
5
5
  dnastack/json_path.py,sha256=TyghhDf7nGQmnsUWBhenU_fKsE_Ez-HLVER6HgH5-hU,2700
6
6
  dnastack/omics_cli.py,sha256=ZppKZTHv_XjUUZyRIzSkx0Ug5ODAYrCOTsU0ezCOVrA,3694
@@ -188,19 +188,19 @@ dnastack/http/session_info.py,sha256=7Fny2U1Zixab4CFGwNsn8Am4tNKYYHF7C1ERG3BhHBI
188
188
  dnastack/http/authenticators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
189
189
  dnastack/http/authenticators/abstract.py,sha256=Q74LnZwsMKs5TfGI_4oZtXBCpgJKkkimkv_Y8YPHuoM,9246
190
190
  dnastack/http/authenticators/constants.py,sha256=mSpBnm5lMMmMJwr13KIfCoOXXJLP4qGDkFprYXALu8o,1278
191
- dnastack/http/authenticators/factory.py,sha256=Kt6PH0-Umfa4ah-Jn8fZthUp2XXKyguhebj3-B0fAZc,2064
192
- dnastack/http/authenticators/oauth2.py,sha256=yZ_bBaIVscDZC25f2vwBptkdcLwX47nFakhL6PW28lM,20071
191
+ dnastack/http/authenticators/factory.py,sha256=zjV5xMZKlkKqUHA2QDXmut-2HxTnqAz8vwxTBEkMg60,2627
192
+ dnastack/http/authenticators/oauth2.py,sha256=6shpSY9v7Ccs6Iy4GjWgx22uq9r_wkEg3z4xeWlHlxE,20298
193
193
  dnastack/http/authenticators/oauth2_adapter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
194
194
  dnastack/http/authenticators/oauth2_adapter/abstract.py,sha256=Tm4Nnroo5_vp0UgZHhcEDVRRbhIrvVdfPr8nTyihoH4,2832
195
195
  dnastack/http/authenticators/oauth2_adapter/client_credential.py,sha256=2iVOof4YZEVGizDRDBISbv8U_xIemtUnHzzxa3mxswc,2770
196
196
  dnastack/http/authenticators/oauth2_adapter/cloud_providers.py,sha256=UHQ-YHHr5ipqSQVzCfr95Uv3zkFcop_RCpK4q6a2yJg,4317
197
197
  dnastack/http/authenticators/oauth2_adapter/device_code_flow.py,sha256=dXI5CyUcsqYg6gf5vDC_3eY6Cc-H1C8W7FeD_24j92A,6750
198
198
  dnastack/http/authenticators/oauth2_adapter/factory.py,sha256=ZtNXOklWEim-26ooNoPp3ji_hRg1vf4fHHnY94F0wLI,1087
199
- dnastack/http/authenticators/oauth2_adapter/models.py,sha256=Dry1supZCVe4aR_0mGdVry5Ctwnvz_mpa1wsGZYloCw,1048
199
+ dnastack/http/authenticators/oauth2_adapter/models.py,sha256=7UsC8DkStMBx7Bz_xHQNi_J1UO_H4nfHme7oBhokj2I,1339
200
200
  dnastack/http/authenticators/oauth2_adapter/token_exchange.py,sha256=nSuAsSKWa_UNqHSbPMOEk4komaFITYAnE04Sk5WOrLc,6332
201
- dnastack_client_library-3.1.205.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
202
- dnastack_client_library-3.1.205.dist-info/METADATA,sha256=tpMwOSCq7qzAEN1nu82BVY4Djq3w46weU0sBkvaa84U,1761
203
- dnastack_client_library-3.1.205.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
204
- dnastack_client_library-3.1.205.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
205
- dnastack_client_library-3.1.205.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
206
- dnastack_client_library-3.1.205.dist-info/RECORD,,
201
+ dnastack_client_library-3.1.208.dist-info/licenses/LICENSE,sha256=uwybO-wUbQhxkosgjhJlxmYATMy-AzoULFO9FUedE34,11580
202
+ dnastack_client_library-3.1.208.dist-info/METADATA,sha256=U-cQKV_41yMifjZPSaIVaXO_I6EuX7366ZFnMJ4UfBI,1761
203
+ dnastack_client_library-3.1.208.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
204
+ dnastack_client_library-3.1.208.dist-info/entry_points.txt,sha256=Y6OeicsiyGn3-8D-SiV4NiKlJgXfkSqK88kFBR6R1rY,89
205
+ dnastack_client_library-3.1.208.dist-info/top_level.txt,sha256=P2RgRyqJ7hfNy1wLVRoVLJYEppUVkCX3syGK9zBqkt8,9
206
+ dnastack_client_library-3.1.208.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5