eodag 2.12.1__py3-none-any.whl → 3.0.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.
Files changed (93) hide show
  1. eodag/__init__.py +6 -8
  2. eodag/api/core.py +654 -538
  3. eodag/api/product/__init__.py +12 -2
  4. eodag/api/product/_assets.py +59 -16
  5. eodag/api/product/_product.py +100 -93
  6. eodag/api/product/drivers/__init__.py +7 -2
  7. eodag/api/product/drivers/base.py +0 -3
  8. eodag/api/product/metadata_mapping.py +192 -96
  9. eodag/api/search_result.py +69 -10
  10. eodag/cli.py +55 -25
  11. eodag/config.py +391 -116
  12. eodag/plugins/apis/base.py +11 -168
  13. eodag/plugins/apis/ecmwf.py +36 -25
  14. eodag/plugins/apis/usgs.py +80 -35
  15. eodag/plugins/authentication/aws_auth.py +13 -4
  16. eodag/plugins/authentication/base.py +10 -1
  17. eodag/plugins/authentication/generic.py +2 -2
  18. eodag/plugins/authentication/header.py +31 -6
  19. eodag/plugins/authentication/keycloak.py +17 -84
  20. eodag/plugins/authentication/oauth.py +3 -3
  21. eodag/plugins/authentication/openid_connect.py +268 -49
  22. eodag/plugins/authentication/qsauth.py +4 -1
  23. eodag/plugins/authentication/sas_auth.py +9 -2
  24. eodag/plugins/authentication/token.py +98 -47
  25. eodag/plugins/authentication/token_exchange.py +122 -0
  26. eodag/plugins/crunch/base.py +3 -1
  27. eodag/plugins/crunch/filter_date.py +3 -9
  28. eodag/plugins/crunch/filter_latest_intersect.py +0 -3
  29. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -4
  30. eodag/plugins/crunch/filter_overlap.py +4 -8
  31. eodag/plugins/crunch/filter_property.py +5 -11
  32. eodag/plugins/download/aws.py +149 -185
  33. eodag/plugins/download/base.py +88 -97
  34. eodag/plugins/download/creodias_s3.py +1 -1
  35. eodag/plugins/download/http.py +638 -310
  36. eodag/plugins/download/s3rest.py +47 -45
  37. eodag/plugins/manager.py +228 -88
  38. eodag/plugins/search/__init__.py +36 -0
  39. eodag/plugins/search/base.py +239 -30
  40. eodag/plugins/search/build_search_result.py +382 -37
  41. eodag/plugins/search/cop_marine.py +441 -0
  42. eodag/plugins/search/creodias_s3.py +25 -20
  43. eodag/plugins/search/csw.py +5 -7
  44. eodag/plugins/search/data_request_search.py +61 -30
  45. eodag/plugins/search/qssearch.py +713 -255
  46. eodag/plugins/search/static_stac_search.py +106 -40
  47. eodag/resources/ext_product_types.json +1 -1
  48. eodag/resources/product_types.yml +1921 -34
  49. eodag/resources/providers.yml +4091 -3655
  50. eodag/resources/stac.yml +50 -216
  51. eodag/resources/stac_api.yml +71 -25
  52. eodag/resources/stac_provider.yml +5 -0
  53. eodag/resources/user_conf_template.yml +89 -32
  54. eodag/rest/__init__.py +6 -0
  55. eodag/rest/cache.py +70 -0
  56. eodag/rest/config.py +68 -0
  57. eodag/rest/constants.py +26 -0
  58. eodag/rest/core.py +735 -0
  59. eodag/rest/errors.py +178 -0
  60. eodag/rest/server.py +264 -431
  61. eodag/rest/stac.py +442 -836
  62. eodag/rest/types/collections_search.py +44 -0
  63. eodag/rest/types/eodag_search.py +238 -47
  64. eodag/rest/types/queryables.py +164 -0
  65. eodag/rest/types/stac_search.py +273 -0
  66. eodag/rest/utils/__init__.py +216 -0
  67. eodag/rest/utils/cql_evaluate.py +119 -0
  68. eodag/rest/utils/rfc3339.py +64 -0
  69. eodag/types/__init__.py +106 -10
  70. eodag/types/bbox.py +15 -14
  71. eodag/types/download_args.py +40 -0
  72. eodag/types/search_args.py +57 -7
  73. eodag/types/whoosh.py +79 -0
  74. eodag/utils/__init__.py +110 -91
  75. eodag/utils/constraints.py +37 -45
  76. eodag/utils/exceptions.py +39 -22
  77. eodag/utils/import_system.py +0 -4
  78. eodag/utils/logging.py +37 -80
  79. eodag/utils/notebook.py +4 -4
  80. eodag/utils/repr.py +113 -0
  81. eodag/utils/requests.py +128 -0
  82. eodag/utils/rest.py +100 -0
  83. eodag/utils/stac_reader.py +93 -21
  84. {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/METADATA +88 -53
  85. eodag-3.0.0.dist-info/RECORD +109 -0
  86. {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/WHEEL +1 -1
  87. {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/entry_points.txt +7 -5
  88. eodag/plugins/apis/cds.py +0 -540
  89. eodag/rest/types/stac_queryables.py +0 -134
  90. eodag/rest/utils.py +0 -1133
  91. eodag-2.12.1.dist-info/RECORD +0 -94
  92. {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/LICENSE +0 -0
  93. {eodag-2.12.1.dist-info → eodag-3.0.0.dist-info}/top_level.txt +0 -0
@@ -17,10 +17,12 @@
17
17
  # limitations under the License.
18
18
  from __future__ import annotations
19
19
 
20
+ import logging
20
21
  import re
21
22
  import string
23
+ from datetime import datetime
22
24
  from random import SystemRandom
23
- from typing import TYPE_CHECKING, Any, Dict, Optional
25
+ from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict
24
26
 
25
27
  import requests
26
28
  from lxml import etree
@@ -36,7 +38,133 @@ if TYPE_CHECKING:
36
38
  from eodag.config import PluginConfig
37
39
 
38
40
 
39
- class OIDCAuthorizationCodeFlowAuth(Authentication):
41
+ logger = logging.getLogger("eodag.auth.openid_connect")
42
+
43
+
44
+ class OIDCRefreshTokenBase(Authentication):
45
+ """OIDC refresh token base class, to be used through specific OIDC flows plugins.
46
+
47
+ Common mechanism to handle refresh token from all OIDC auth plugins.
48
+ """
49
+
50
+ class TokenInfo(TypedDict, total=False):
51
+ """Token infos"""
52
+
53
+ refresh_token: str
54
+ refresh_time: datetime
55
+ token_time: datetime
56
+ access_token: str
57
+ access_token_expiration: float
58
+ refresh_token_expiration: float
59
+
60
+ def __init__(self, provider: str, config: PluginConfig) -> None:
61
+ super(OIDCRefreshTokenBase, self).__init__(provider, config)
62
+ self.session = requests.Session()
63
+ # already retrieved token info store
64
+ self.token_info: OIDCRefreshTokenBase.TokenInfo = {}
65
+
66
+ def _get_access_token(self) -> str:
67
+ current_time = datetime.now()
68
+ if (
69
+ # No info: first time
70
+ not self.token_info
71
+ # Refresh token available but expired
72
+ or (
73
+ "refresh_token" in self.token_info
74
+ and self.token_info["refresh_token_expiration"] > 0
75
+ and (current_time - self.token_info["token_time"]).seconds
76
+ >= self.token_info["refresh_token_expiration"]
77
+ )
78
+ # Refresh token *not* available and access token expired
79
+ or (
80
+ "refresh_token" not in self.token_info
81
+ and (current_time - self.token_info["token_time"]).seconds
82
+ >= self.token_info["access_token_expiration"]
83
+ )
84
+ ):
85
+ # Request *new* token on first attempt or if token expired
86
+ res = self._request_new_token()
87
+ self.token_info["token_time"] = current_time
88
+ self.token_info["access_token_expiration"] = float(res["expires_in"])
89
+ if "refresh_token" in res:
90
+ self.token_info["refresh_time"] = current_time
91
+ self.token_info["refresh_token_expiration"] = float(
92
+ res["refresh_expires_in"]
93
+ )
94
+ self.token_info["refresh_token"] = str(res["refresh_token"])
95
+ return str(res["access_token"])
96
+
97
+ elif (
98
+ # Refresh token available and access token expired
99
+ "refresh_token" in self.token_info
100
+ and (current_time - self.token_info["refresh_time"]).seconds
101
+ >= self.token_info["access_token_expiration"]
102
+ ):
103
+ # Use refresh token
104
+ res = self._get_token_with_refresh_token()
105
+ self.token_info["refresh_token"] = res["refresh_token"]
106
+ self.token_info["refresh_time"] = current_time
107
+ return res["access_token"]
108
+
109
+ logger.debug("Using already retrieved access token")
110
+ return self.token_info["access_token"]
111
+
112
+ def _request_new_token(self) -> Dict[str, str]:
113
+ """Fetch the access token with a new authentcation"""
114
+ raise NotImplementedError(
115
+ "Incomplete OIDC refresh token retrieval mechanism implementation"
116
+ )
117
+
118
+ def _request_new_token_error(self, e: requests.RequestException) -> Dict[str, str]:
119
+ """Handle RequestException raised by `self._request_new_token()`"""
120
+ if self.token_info.get("access_token"):
121
+ # try using already retrieved token if authenticate() fails (OTP use-case)
122
+ if "access_token_expiration" in self.token_info:
123
+ return {
124
+ "access_token": self.token_info["access_token"],
125
+ "expires_in": str(self.token_info["access_token_expiration"]),
126
+ }
127
+ else:
128
+ return {
129
+ "access_token": self.token_info["access_token"],
130
+ "expires_in": "0",
131
+ }
132
+ response_text = getattr(e.response, "text", "").strip()
133
+ # check if error is identified as auth_error in provider conf
134
+ auth_errors = getattr(self.config, "auth_error_code", [None])
135
+ if not isinstance(auth_errors, list):
136
+ auth_errors = [auth_errors]
137
+ if (
138
+ e.response
139
+ and hasattr(e.response, "status_code")
140
+ and e.response.status_code in auth_errors
141
+ ):
142
+ raise AuthenticationError(
143
+ f"Please check your credentials for {self.provider}.",
144
+ f"HTTP Error {e.response.status_code} returned.",
145
+ response_text,
146
+ )
147
+ # other error
148
+ else:
149
+ import traceback as tb
150
+
151
+ logger.error(
152
+ f"Provider {self.provider} returned {getattr(e.response, 'status_code', '')}: {response_text}"
153
+ )
154
+ raise AuthenticationError(
155
+ "Something went wrong while trying to get access token:\n{}".format(
156
+ tb.format_exc()
157
+ )
158
+ )
159
+
160
+ def _get_token_with_refresh_token(self) -> Dict[str, str]:
161
+ """Fetch the access token with the refresh token"""
162
+ raise NotImplementedError(
163
+ "Incomplete OIDC refresh token retrieval mechanism implementation"
164
+ )
165
+
166
+
167
+ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
40
168
  """Implement the authorization code flow of the OpenIDConnect authorization specification.
41
169
 
42
170
  The `OpenID Connect <http://openid.net/specs/openid-connect-core-1_0.html>`_ specification
@@ -109,6 +237,10 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
109
237
  # same rules as with user_consent_form_data
110
238
  additional_login_form_data:
111
239
 
240
+ # (optional) Key/value pairs of patterns/messages. If exchange_url contains the given pattern, the associated
241
+ message will be sent in an AuthenticationError
242
+ exchange_url_error_pattern:
243
+
112
244
  # (optional) The OIDC provider's client secret of the eodag provider
113
245
  client_secret:
114
246
 
@@ -123,6 +255,8 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
123
255
  # used in the query request
124
256
  token_qs_key:
125
257
 
258
+ # (optional) The key pointing to the refresh_token in the json response to the POST request to the token server
259
+ refresh_token_key:
126
260
  """
127
261
 
128
262
  SCOPE = "openid"
@@ -131,6 +265,10 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
131
265
 
132
266
  def __init__(self, provider: str, config: PluginConfig) -> None:
133
267
  super(OIDCAuthorizationCodeFlowAuth, self).__init__(provider, config)
268
+
269
+ def validate_config_credentials(self) -> None:
270
+ """Validate configured credentials"""
271
+ super(OIDCAuthorizationCodeFlowAuth, self).validate_config_credentials()
134
272
  if getattr(self.config, "token_provision", None) not in ("qs", "header"):
135
273
  raise MisconfiguredError(
136
274
  'Provider config parameter "token_provision" must be one of "qs" or "header"'
@@ -142,33 +280,76 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
142
280
  'Provider config parameter "token_provision" with value "qs" must have '
143
281
  '"token_qs_key" config parameter as well'
144
282
  )
145
- self.session = requests.Session()
146
283
 
147
- def authenticate(self) -> AuthBase:
284
+ def authenticate(self) -> CodeAuthorizedAuth:
148
285
  """Authenticate"""
286
+ self.token_info["access_token"] = self._get_access_token()
287
+
288
+ return CodeAuthorizedAuth(
289
+ self.token_info["access_token"],
290
+ self.config.token_provision,
291
+ key=getattr(self.config, "token_qs_key", None),
292
+ )
293
+
294
+ def _request_new_token(self) -> Dict[str, str]:
295
+ """Fetch the access token with a new authentcation"""
296
+ logger.debug("Fetching access token from %s", self.config.token_uri)
149
297
  state = self.compute_state()
150
298
  authentication_response = self.authenticate_user(state)
151
299
  exchange_url = authentication_response.url
300
+ for err_pattern, err_message in getattr(
301
+ self.config, "exchange_url_error_pattern", {}
302
+ ).items():
303
+ if err_pattern in exchange_url:
304
+ raise AuthenticationError(err_message)
305
+ if not exchange_url.startswith(self.config.redirect_uri):
306
+ raise AuthenticationError(
307
+ f"Could not authenticate user with provider {self.provider}.",
308
+ "Please verify your credentials",
309
+ )
152
310
  if self.config.user_consent_needed:
153
311
  user_consent_response = self.grant_user_consent(authentication_response)
154
312
  exchange_url = user_consent_response.url
155
313
  try:
156
- token = self.exchange_code_for_token(exchange_url, state)
314
+ token_response = self.exchange_code_for_token(exchange_url, state)
315
+ token_response.raise_for_status()
157
316
  except requests.exceptions.Timeout as exc:
158
317
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
159
- except Exception:
160
- import traceback as tb
161
-
162
- raise AuthenticationError(
163
- "Something went wrong while trying to get authorization token:\n{}".format(
164
- tb.format_exc()
165
- )
166
- )
167
- return CodeAuthorizedAuth(
168
- token,
169
- self.config.token_provision,
170
- key=getattr(self.config, "token_qs_key", None),
318
+ except requests.RequestException as e:
319
+ return self._request_new_token_error(e)
320
+ return token_response.json()
321
+
322
+ def _get_token_with_refresh_token(self) -> Dict[str, str]:
323
+ """Fetch the access token with the refresh token"""
324
+ logger.debug(
325
+ "Fetching access token with refresh token from %s", self.config.token_uri
171
326
  )
327
+ token_data: Dict[str, Any] = {
328
+ "refresh_token": self.token_info["refresh_token"],
329
+ "grant_type": "refresh_token",
330
+ }
331
+ token_data = self._prepare_token_post_data(token_data)
332
+ post_request_kwargs: Any = {
333
+ self.config.token_exchange_post_data_method: token_data
334
+ }
335
+ ssl_verify = getattr(self.config, "ssl_verify", True)
336
+ try:
337
+ token_response = self.session.post(
338
+ self.config.token_uri,
339
+ timeout=HTTP_REQ_TIMEOUT,
340
+ verify=ssl_verify,
341
+ **post_request_kwargs,
342
+ )
343
+ token_response.raise_for_status()
344
+ except requests.exceptions.Timeout as exc:
345
+ raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
346
+ except requests.RequestException as exc:
347
+ logger.error(
348
+ "Could not fetch access token with refresh token, executing new token request, error: %s",
349
+ getattr(exc.response, "text", ""),
350
+ )
351
+ return self._request_new_token()
352
+ return token_response.json()
172
353
 
173
354
  def authenticate_user(self, state: str) -> Response:
174
355
  """Authenticate user"""
@@ -180,15 +361,19 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
180
361
  "state": state,
181
362
  "redirect_uri": self.config.redirect_uri,
182
363
  }
364
+ ssl_verify = getattr(self.config, "ssl_verify", True)
183
365
  authorization_response = self.session.get(
184
366
  self.config.authorization_uri,
185
367
  params=params,
186
368
  headers=USER_AGENT,
187
369
  timeout=HTTP_REQ_TIMEOUT,
370
+ verify=ssl_verify,
188
371
  )
189
372
 
190
373
  login_document = etree.HTML(authorization_response.text)
191
- login_form = login_document.xpath(self.config.login_form_xpath)[0]
374
+ login_forms = login_document.xpath(self.config.login_form_xpath)
375
+ login_form = login_forms[0]
376
+
192
377
  # Get the form data to pass to the login form from config or from the login form
193
378
  login_data = {
194
379
  key: self._constant_or_xpath_extracted(value, login_form)
@@ -198,16 +383,29 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
198
383
  }
199
384
  # Add the credentials
200
385
  login_data.update(self.config.credentials)
201
- auth_uri = getattr(self.config, "authentication_uri", None)
386
+
202
387
  # Retrieve the authentication_uri from the login form if so configured
203
388
  if self.config.authentication_uri_source == "login-form":
204
389
  # Given that the login_form_xpath resolves to an HTML element, if suffices to add '/@action' to get
205
390
  # the value of its action attribute to this xpath
206
391
  auth_uri = login_form.xpath(
207
392
  self.config.login_form_xpath.rstrip("/") + "/@action"
208
- )[0]
393
+ )
394
+ if not auth_uri or not auth_uri[0]:
395
+ raise MisconfiguredError(
396
+ f"Could not get auth_uri from {self.config.login_form_xpath}"
397
+ )
398
+ auth_uri = auth_uri[0]
399
+ else:
400
+ auth_uri = getattr(self.config, "authentication_uri", None)
401
+ if not auth_uri:
402
+ raise MisconfiguredError("authentication_uri is missing")
209
403
  return self.session.post(
210
- auth_uri, data=login_data, headers=USER_AGENT, timeout=HTTP_REQ_TIMEOUT
404
+ auth_uri,
405
+ data=login_data,
406
+ headers=USER_AGENT,
407
+ timeout=HTTP_REQ_TIMEOUT,
408
+ verify=ssl_verify,
211
409
  )
212
410
 
213
411
  def grant_user_consent(self, authentication_response: Response) -> Response:
@@ -221,56 +419,69 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
221
419
  key: self._constant_or_xpath_extracted(value, user_consent_form)
222
420
  for key, value in self.config.user_consent_form_data.items()
223
421
  }
422
+ ssl_verify = getattr(self.config, "ssl_verify", True)
224
423
  return self.session.post(
225
424
  self.config.authorization_uri,
226
425
  data=user_consent_data,
227
426
  headers=USER_AGENT,
228
427
  timeout=HTTP_REQ_TIMEOUT,
428
+ verify=ssl_verify,
229
429
  )
230
430
 
231
- def exchange_code_for_token(self, authorized_url: str, state: str) -> str:
232
- """Get exchange code for token"""
233
- qs = parse_qs(urlparse(authorized_url).query)
234
- if qs["state"][0] != state:
235
- raise AuthenticationError(
236
- "The state received in the authorized url does not match initially computed state"
237
- )
238
- code = qs["code"][0]
239
- token_exchange_data: Dict[str, Any] = {
240
- "redirect_uri": self.config.redirect_uri,
241
- "client_id": self.config.client_id,
242
- "code": code,
243
- "state": state,
244
- }
431
+ def _prepare_token_post_data(self, token_data: Dict[str, Any]) -> Dict[str, Any]:
432
+ """Prepare the common data to post to the token URI"""
433
+ token_data.update(
434
+ {
435
+ "redirect_uri": self.config.redirect_uri,
436
+ "client_id": self.config.client_id,
437
+ }
438
+ )
245
439
  # If necessary, change the keys of the form data that will be passed to the token exchange POST request
246
440
  custom_token_exchange_params = getattr(self.config, "token_exchange_params", {})
247
441
  if custom_token_exchange_params:
248
- token_exchange_data[
249
- custom_token_exchange_params["redirect_uri"]
250
- ] = token_exchange_data.pop("redirect_uri")
251
- token_exchange_data[
252
- custom_token_exchange_params["client_id"]
253
- ] = token_exchange_data.pop("client_id")
442
+ token_data[custom_token_exchange_params["redirect_uri"]] = token_data.pop(
443
+ "redirect_uri"
444
+ )
445
+ token_data[custom_token_exchange_params["client_id"]] = token_data.pop(
446
+ "client_id"
447
+ )
254
448
  # If the client_secret is known, the token exchange request must be authenticated with a BASIC Auth, using the
255
449
  # client_id and client_secret as username and password respectively
256
450
  if getattr(self.config, "client_secret", None):
257
- token_exchange_data.update(
451
+ token_data.update(
258
452
  {
259
453
  "auth": (self.config.client_id, self.config.client_secret),
260
- "grant_type": "authorization_code",
261
454
  "client_secret": self.config.client_secret,
262
455
  }
263
456
  )
264
- post_request_kwargs = {
457
+ return token_data
458
+
459
+ def exchange_code_for_token(self, authorized_url: str, state: str) -> Response:
460
+ """Get exchange code for token"""
461
+ qs = parse_qs(urlparse(authorized_url).query)
462
+ if qs["state"][0] != state:
463
+ raise AuthenticationError(
464
+ "The state received in the authorized url does not match initially computed state"
465
+ )
466
+ code = qs["code"][0]
467
+ token_exchange_data: Dict[str, Any] = {
468
+ "code": code,
469
+ "state": state,
470
+ "grant_type": "authorization_code",
471
+ }
472
+ token_exchange_data = self._prepare_token_post_data(token_exchange_data)
473
+ post_request_kwargs: Any = {
265
474
  self.config.token_exchange_post_data_method: token_exchange_data
266
475
  }
476
+ ssl_verify = getattr(self.config, "ssl_verify", True)
267
477
  r = self.session.post(
268
478
  self.config.token_uri,
269
479
  headers=USER_AGENT,
270
480
  timeout=HTTP_REQ_TIMEOUT,
481
+ verify=ssl_verify,
271
482
  **post_request_kwargs,
272
483
  )
273
- return r.json()[self.config.token_key]
484
+ return r
274
485
 
275
486
  def _constant_or_xpath_extracted(
276
487
  self, value: str, form_element: Any
@@ -279,7 +490,7 @@ class OIDCAuthorizationCodeFlowAuth(Authentication):
279
490
  if not match:
280
491
  return value
281
492
  value_from_xpath = form_element.xpath(
282
- self.CONFIG_XPATH_REGEX.match(value).groupdict("xpath_value")
493
+ match.groupdict("xpath_value")["xpath_value"]
283
494
  )
284
495
  if len(value_from_xpath) == 1:
285
496
  return value_from_xpath[0]
@@ -309,13 +520,21 @@ class CodeAuthorizedAuth(AuthBase):
309
520
  def __call__(self, request: PreparedRequest) -> PreparedRequest:
310
521
  """Perform the actual authentication"""
311
522
  if self.where == "qs":
312
- parts = urlparse(request.url)
523
+ parts = urlparse(str(request.url))
313
524
  query_dict = parse_qs(parts.query)
314
- query_dict.update({self.key: self.token})
315
- url_without_args = parts._replace(query=None).geturl()
525
+ if self.key is not None:
526
+ query_dict.update({self.key: [self.token]})
527
+ url_without_args = parts._replace(query="").geturl()
316
528
 
317
529
  request.prepare_url(url_without_args, query_dict)
318
530
 
319
531
  elif self.where == "header":
320
532
  request.headers["Authorization"] = "Bearer {}".format(self.token)
533
+ logger.debug(
534
+ re.sub(
535
+ r"'Bearer [^']+'",
536
+ r"'Bearer ***'",
537
+ f"PreparedRequest: {request.__dict__}",
538
+ )
539
+ )
321
540
  return request
@@ -67,6 +67,8 @@ class HttpQueryStringAuth(Authentication):
67
67
  auth = QueryStringAuth(**self.config.credentials)
68
68
 
69
69
  auth_uri = getattr(self.config, "auth_uri", None)
70
+ ssl_verify = getattr(self.config, "ssl_verify", True)
71
+
70
72
  if auth_uri:
71
73
  try:
72
74
  response = requests.get(
@@ -74,12 +76,13 @@ class HttpQueryStringAuth(Authentication):
74
76
  timeout=HTTP_REQ_TIMEOUT,
75
77
  headers=USER_AGENT,
76
78
  auth=auth,
79
+ verify=ssl_verify,
77
80
  )
78
81
  response.raise_for_status()
79
82
  except requests.exceptions.Timeout as exc:
80
83
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
81
84
  except RequestException as e:
82
- raise AuthenticationError(f"Could no authenticate: {str(e)}")
85
+ raise AuthenticationError("Could no authenticate", str(e)) from e
83
86
 
84
87
  return auth
85
88
 
@@ -43,11 +43,13 @@ class RequestsSASAuth(AuthBase):
43
43
  auth_uri: str,
44
44
  signed_url_key: str,
45
45
  headers: Optional[Dict[str, str]] = None,
46
+ ssl_verify: bool = True,
46
47
  ) -> None:
47
48
  self.auth_uri = auth_uri
48
49
  self.signed_url_key = signed_url_key
49
50
  self.headers = headers
50
51
  self.signed_urls: Dict[str, str] = {}
52
+ self.ssl_verify = ssl_verify
51
53
 
52
54
  def __call__(self, request: PreparedRequest) -> PreparedRequest:
53
55
  """Perform the actual authentication"""
@@ -63,14 +65,17 @@ class RequestsSASAuth(AuthBase):
63
65
  logger.debug(f"Signed URL request: {req_signed_url}")
64
66
  try:
65
67
  response = requests.get(
66
- req_signed_url, headers=self.headers, timeout=HTTP_REQ_TIMEOUT
68
+ req_signed_url,
69
+ headers=self.headers,
70
+ timeout=HTTP_REQ_TIMEOUT,
71
+ verify=self.ssl_verify,
67
72
  )
68
73
  response.raise_for_status()
69
74
  signed_url = response.json().get(self.signed_url_key)
70
75
  except requests.exceptions.Timeout as exc:
71
76
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
72
77
  except (requests.RequestException, JSONDecodeError, KeyError) as e:
73
- raise AuthenticationError(f"Could no get signed url: {str(e)}")
78
+ raise AuthenticationError("Could no get signed url", str(e)) from e
74
79
  else:
75
80
  self.signed_urls[req_signed_url] = signed_url
76
81
 
@@ -95,6 +100,7 @@ class SASAuth(Authentication):
95
100
 
96
101
  # update headers with subscription key if exists
97
102
  apikey = getattr(self.config, "credentials", {}).get("apikey", None)
103
+ ssl_verify = getattr(self.config, "ssl_verify", True)
98
104
  if apikey:
99
105
  headers_update = format_dict_items(self.config.headers, apikey=apikey)
100
106
  headers.update(headers_update)
@@ -103,4 +109,5 @@ class SASAuth(Authentication):
103
109
  auth_uri=self.config.auth_uri,
104
110
  signed_url_key=self.config.signed_url_key,
105
111
  headers=headers,
112
+ ssl_verify=ssl_verify,
106
113
  )