eodag 3.0.0b3__py3-none-any.whl → 3.1.0b1__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 (77) hide show
  1. eodag/api/core.py +292 -198
  2. eodag/api/product/_assets.py +6 -6
  3. eodag/api/product/_product.py +18 -18
  4. eodag/api/product/metadata_mapping.py +51 -14
  5. eodag/api/search_result.py +29 -3
  6. eodag/cli.py +57 -20
  7. eodag/config.py +413 -117
  8. eodag/plugins/apis/base.py +10 -4
  9. eodag/plugins/apis/ecmwf.py +49 -16
  10. eodag/plugins/apis/usgs.py +30 -7
  11. eodag/plugins/authentication/aws_auth.py +14 -5
  12. eodag/plugins/authentication/base.py +10 -1
  13. eodag/plugins/authentication/generic.py +14 -3
  14. eodag/plugins/authentication/header.py +12 -4
  15. eodag/plugins/authentication/keycloak.py +41 -22
  16. eodag/plugins/authentication/oauth.py +11 -1
  17. eodag/plugins/authentication/openid_connect.py +178 -163
  18. eodag/plugins/authentication/qsauth.py +12 -4
  19. eodag/plugins/authentication/sas_auth.py +19 -2
  20. eodag/plugins/authentication/token.py +93 -15
  21. eodag/plugins/authentication/token_exchange.py +19 -19
  22. eodag/plugins/crunch/base.py +4 -1
  23. eodag/plugins/crunch/filter_date.py +5 -2
  24. eodag/plugins/crunch/filter_latest_intersect.py +5 -4
  25. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  26. eodag/plugins/crunch/filter_overlap.py +5 -7
  27. eodag/plugins/crunch/filter_property.py +6 -6
  28. eodag/plugins/download/aws.py +50 -34
  29. eodag/plugins/download/base.py +41 -50
  30. eodag/plugins/download/creodias_s3.py +40 -2
  31. eodag/plugins/download/http.py +221 -195
  32. eodag/plugins/download/s3rest.py +25 -25
  33. eodag/plugins/manager.py +168 -23
  34. eodag/plugins/search/base.py +106 -39
  35. eodag/plugins/search/build_search_result.py +1065 -324
  36. eodag/plugins/search/cop_marine.py +112 -29
  37. eodag/plugins/search/creodias_s3.py +45 -24
  38. eodag/plugins/search/csw.py +41 -1
  39. eodag/plugins/search/data_request_search.py +109 -9
  40. eodag/plugins/search/qssearch.py +549 -257
  41. eodag/plugins/search/static_stac_search.py +20 -21
  42. eodag/resources/ext_product_types.json +1 -1
  43. eodag/resources/product_types.yml +577 -87
  44. eodag/resources/providers.yml +1619 -2776
  45. eodag/resources/stac.yml +3 -163
  46. eodag/resources/user_conf_template.yml +112 -97
  47. eodag/rest/config.py +1 -2
  48. eodag/rest/constants.py +0 -1
  49. eodag/rest/core.py +138 -98
  50. eodag/rest/errors.py +181 -0
  51. eodag/rest/server.py +55 -329
  52. eodag/rest/stac.py +93 -544
  53. eodag/rest/types/eodag_search.py +19 -8
  54. eodag/rest/types/queryables.py +6 -8
  55. eodag/rest/types/stac_search.py +11 -2
  56. eodag/rest/utils/__init__.py +3 -0
  57. eodag/types/__init__.py +71 -18
  58. eodag/types/download_args.py +3 -3
  59. eodag/types/queryables.py +180 -73
  60. eodag/types/search_args.py +3 -3
  61. eodag/types/whoosh.py +126 -0
  62. eodag/utils/__init__.py +147 -66
  63. eodag/utils/exceptions.py +47 -26
  64. eodag/utils/logging.py +37 -77
  65. eodag/utils/repr.py +65 -6
  66. eodag/utils/requests.py +11 -13
  67. eodag/utils/stac_reader.py +1 -1
  68. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/METADATA +80 -81
  69. eodag-3.1.0b1.dist-info/RECORD +108 -0
  70. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/WHEEL +1 -1
  71. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/entry_points.txt +4 -2
  72. eodag/resources/constraints/climate-dt.json +0 -13
  73. eodag/resources/constraints/extremes-dt.json +0 -8
  74. eodag/utils/constraints.py +0 -244
  75. eodag-3.0.0b3.dist-info/RECORD +0 -110
  76. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/LICENSE +0 -0
  77. {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/top_level.txt +0 -0
@@ -20,22 +20,18 @@ from __future__ import annotations
20
20
  import logging
21
21
  import re
22
22
  import string
23
- from datetime import datetime
23
+ from datetime import datetime, timedelta, timezone
24
24
  from random import SystemRandom
25
- from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict
25
+ from typing import TYPE_CHECKING, Any, Dict, Optional
26
26
 
27
+ import jwt
27
28
  import requests
28
29
  from lxml import etree
29
30
  from requests.auth import AuthBase
30
31
 
31
32
  from eodag.plugins.authentication import Authentication
32
33
  from eodag.utils import HTTP_REQ_TIMEOUT, USER_AGENT, parse_qs, repeatfunc, urlparse
33
- from eodag.utils.exceptions import (
34
- AuthenticationError,
35
- MisconfiguredError,
36
- RequestError,
37
- TimeOutError,
38
- )
34
+ from eodag.utils.exceptions import AuthenticationError, MisconfiguredError, TimeOutError
39
35
 
40
36
  if TYPE_CHECKING:
41
37
  from requests import PreparedRequest, Response
@@ -47,93 +43,121 @@ logger = logging.getLogger("eodag.auth.openid_connect")
47
43
 
48
44
 
49
45
  class OIDCRefreshTokenBase(Authentication):
50
- """OIDC refresh token base class, to be used through specific OIDC flows plugins.
46
+ """OIDC refresh token base class, to be used through specific OIDC flows plugins;
47
+ Common mechanism to handle refresh token from all OIDC auth plugins;
48
+
49
+ Plugins inheriting from this base class must implement the methods ``_request_new_token()`` and
50
+ ``_get_token_with_refresh_token()``. Depending on the implementation of these methods they can have
51
+ different configuration parameters.
51
52
 
52
- Common mechanism to handle refresh token from all OIDC auth plugins.
53
53
  """
54
54
 
55
- class TokenInfo(TypedDict, total=False):
56
- """Token infos"""
55
+ jwks_client: jwt.PyJWKClient
56
+
57
+ access_token: str
58
+ access_token_expiration: datetime
57
59
 
58
- refresh_token: str
59
- refresh_time: datetime
60
- token_time: datetime
61
- access_token: str
62
- access_token_expiration: float
63
- refresh_token_expiration: float
60
+ refresh_token: str
61
+ refresh_token_expiration: datetime
62
+
63
+ token_endpoint: str
64
+ authorization_endpoint: str
64
65
 
65
66
  def __init__(self, provider: str, config: PluginConfig) -> None:
66
67
  super(OIDCRefreshTokenBase, self).__init__(provider, config)
67
68
  self.session = requests.Session()
68
- # already retrieved token info store
69
- self.token_info: OIDCRefreshTokenBase.TokenInfo = {}
69
+
70
+ self.access_token = ""
71
+ self.access_token_expiration = datetime.min.replace(tzinfo=timezone.utc)
72
+
73
+ self.refresh_token = ""
74
+ self.refresh_token_expiration = datetime.min.replace(tzinfo=timezone.utc)
75
+
76
+ try:
77
+ response = requests.get(self.config.oidc_config_url)
78
+ response.raise_for_status()
79
+ auth_config = response.json()
80
+ except requests.HTTPError as e:
81
+ raise MisconfiguredError(
82
+ f"Cannot obtain OIDC endpoints from {self.config.oidc_config_url}"
83
+ f"Request returned {e.response.text}."
84
+ )
85
+
86
+ self.jwks_client = jwt.PyJWKClient(auth_config["jwks_uri"])
87
+ self.token_endpoint = auth_config["token_endpoint"]
88
+ self.authorization_endpoint = auth_config["authorization_endpoint"]
89
+ self.algorithms = auth_config["id_token_signing_alg_values_supported"]
90
+
91
+ def decode_jwt_token(self, token: str) -> Dict[str, Any]:
92
+ """Decode JWT token."""
93
+ try:
94
+ key = self.jwks_client.get_signing_key_from_jwt(token).key
95
+ if getattr(self.config, "allowed_audiences", None):
96
+ return jwt.decode(
97
+ token,
98
+ key,
99
+ algorithms=self.algorithms,
100
+ # NOTE: Audience validation MUST match audience claim if set in token
101
+ # (https://pyjwt.readthedocs.io/en/stable/changelog.html?highlight=audience#id40)
102
+ audience=self.config.allowed_audiences,
103
+ )
104
+ else:
105
+ return jwt.decode(
106
+ token,
107
+ key,
108
+ algorithms=self.algorithms,
109
+ )
110
+ except (jwt.exceptions.InvalidTokenError, jwt.exceptions.DecodeError) as e:
111
+ raise AuthenticationError(e)
70
112
 
71
113
  def _get_access_token(self) -> str:
72
- current_time = datetime.now()
73
- if (
74
- # No info: first time
75
- not self.token_info
76
- # Refresh token available but expired
77
- or (
78
- "refresh_token" in self.token_info
79
- and self.token_info["refresh_token_expiration"] > 0
80
- and (current_time - self.token_info["token_time"]).seconds
81
- >= self.token_info["refresh_token_expiration"]
114
+ now = datetime.now(timezone.utc)
115
+ if self.access_token and now < self.access_token_expiration:
116
+ logger.debug(
117
+ f"Existing access_token is still valid until {self.access_token_expiration.isoformat()}."
82
118
  )
83
- # Refresh token *not* available and access token expired
84
- or (
85
- "refresh_token" not in self.token_info
86
- and (current_time - self.token_info["token_time"]).seconds
87
- >= self.token_info["access_token_expiration"]
119
+ return self.access_token
120
+
121
+ elif self.refresh_token and now < self.refresh_token_expiration:
122
+ response = self._get_token_with_refresh_token()
123
+ logger.debug(
124
+ "access_token expired, fetching new access_token using refresh_token"
88
125
  )
89
- ):
90
- # Request *new* token on first attempt or if token expired
91
- res = self._request_new_token()
92
- self.token_info["token_time"] = current_time
93
- self.token_info["access_token_expiration"] = float(res["expires_in"])
94
- if "refresh_token" in res:
95
- self.token_info["refresh_time"] = current_time
96
- self.token_info["refresh_token_expiration"] = float(
97
- res["refresh_expires_in"]
98
- )
99
- self.token_info["refresh_token"] = str(res["refresh_token"])
100
- return str(res["access_token"])
101
-
102
- elif (
103
- # Refresh token available and access token expired
104
- "refresh_token" in self.token_info
105
- and (current_time - self.token_info["refresh_time"]).seconds
106
- >= self.token_info["access_token_expiration"]
107
- ):
108
- # Use refresh token
109
- res = self._get_token_with_refresh_token()
110
- self.token_info["refresh_token"] = res["refresh_token"]
111
- self.token_info["refresh_time"] = current_time
112
- return res["access_token"]
126
+ else:
127
+ logger.debug("access_token expired or not available yet, new token request")
128
+ response = self._request_new_token()
129
+
130
+ self.access_token = response[getattr(self.config, "token_key", "access_token")]
131
+ self.access_token_expiration = datetime.fromtimestamp(
132
+ self.decode_jwt_token(self.access_token)["exp"], timezone.utc
133
+ )
134
+ self.refresh_token = response.get(
135
+ getattr(self.config, "refresh_token_key", "refresh_token"), ""
136
+ )
137
+ if self.refresh_token and response.get("refresh_expires_in", "0"):
138
+ self.refresh_token_expiration = now + timedelta(
139
+ seconds=int(response["refresh_expires_in"])
140
+ )
141
+ else:
142
+ # refresh token does not expire but will be changed at each request
143
+ self.refresh_token_expiration = now + timedelta(days=1000)
113
144
 
114
- logger.debug("Using already retrieved access token")
115
- return self.token_info["access_token"]
145
+ return self.access_token
116
146
 
117
147
  def _request_new_token(self) -> Dict[str, str]:
118
- """Fetch the access token with a new authentcation"""
148
+ """Fetch the access token with a new authentication"""
119
149
  raise NotImplementedError(
120
150
  "Incomplete OIDC refresh token retrieval mechanism implementation"
121
151
  )
122
152
 
123
153
  def _request_new_token_error(self, e: requests.RequestException) -> Dict[str, str]:
124
154
  """Handle RequestException raised by `self._request_new_token()`"""
125
- if self.token_info.get("access_token"):
155
+ if self.access_token:
126
156
  # try using already retrieved token if authenticate() fails (OTP use-case)
127
- if "access_token_expiration" in self.token_info:
128
- return {
129
- "access_token": self.token_info["access_token"],
130
- "expires_in": str(self.token_info["access_token_expiration"]),
131
- }
132
- else:
133
- return {
134
- "access_token": self.token_info["access_token"],
135
- "expires_in": "0",
136
- }
157
+ return {
158
+ "access_token": self.access_token,
159
+ "expires_in": self.access_token_expiration.isoformat(),
160
+ }
137
161
  response_text = getattr(e.response, "text", "").strip()
138
162
  # check if error is identified as auth_error in provider conf
139
163
  auth_errors = getattr(self.config, "auth_error_code", [None])
@@ -145,8 +169,9 @@ class OIDCRefreshTokenBase(Authentication):
145
169
  and e.response.status_code in auth_errors
146
170
  ):
147
171
  raise AuthenticationError(
148
- "HTTP Error %s returned, %s\nPlease check your credentials for %s"
149
- % (e.response.status_code, response_text, self.provider)
172
+ f"Please check your credentials for {self.provider}.",
173
+ f"HTTP Error {e.response.status_code} returned.",
174
+ response_text,
150
175
  )
151
176
  # other error
152
177
  else:
@@ -175,6 +200,7 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
175
200
  adds an authentication layer on top of oauth 2.0. This plugin implements the
176
201
  `authorization code flow <http://openid.net/specs/openid-connect-core-1_0.html#Authentication>`_
177
202
  option of this specification.
203
+
178
204
  The particularity of this plugin is that it proceeds to a headless (not involving the user)
179
205
  interaction with the OpenID provider (if necessary) to authenticate a
180
206
  registered user with its username and password on the server and then granting to eodag the
@@ -184,83 +210,60 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
184
210
  The headless interaction is fully configurable, and rely on XPATH to retrieve all the necessary
185
211
  information.
186
212
 
187
- The configuration keys of this plugin are as follows (they have no defaults)::
188
-
189
- # (mandatory) The authorization url of the server (where to query for grants)
190
- authorization_uri:
191
-
192
- # (mandatory) The callback url that will handle the code given by the OIDC provider
193
- redirect_uri:
194
-
195
- # (mandatory) The url to query to exchange the authorization code obtained from the OIDC provider
196
- # for an authorized token
197
- token_uri:
198
-
199
- # (mandatory) The OIDC provider's client ID of the eodag provider
200
- client_id:
201
-
202
- # (mandatory) Wether a user consent is needed during the authentication
203
- user_consent_needed:
204
-
205
- # (mandatory) One of: json, data or params. This is the way to pass the data to the POST request
206
- # that is made to the token server. They correspond to the recognised keywords arguments
207
- # of the Python `requests <http://docs.python-requests.org/>`_ library
208
- token_exchange_post_data_method:
209
-
210
- # (mandatory) The key pointing to the token in the json response to the POST request to the token server
211
- token_key:
212
-
213
- # (mandatory) One of qs or header. This is how the token obtained will be used to authenticate the user
214
- # on protected requests. If 'qs' is chosen, then 'token_qs_key' is mandatory
215
- token_provision:
213
+ :param provider: provider name
214
+ :param config: Authentication plugin configuration:
215
+
216
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): OIDCAuthorizationCodeFlowAuth
217
+ * :attr:`~eodag.config.PluginConfig.redirect_uri` (``str``) (**mandatory**): The callback
218
+ url that will handle the code given by the OIDC provider
219
+ * :attr:`~eodag.config.PluginConfig.oidc_config_url` (``str``) (**mandatory**):
220
+ The url to get the OIDC Provider's endpoints
221
+ * :attr:`~eodag.config.PluginConfig.client_id` (``str``) (**mandatory**): The OIDC provider's
222
+ client ID of the eodag provider
223
+ * :attr:`~eodag.config.PluginConfig.user_consent_needed` (``bool``) (mandatory): Whether
224
+ a user consent is needed during the authentication
225
+ * :attr:`~eodag.config.PluginConfig.token_exchange_post_data_method` (``str``) (**mandatory**):
226
+ One of: ``json``, ``data`` or ``params``. This is the way to pass the data to the
227
+ POST request that is made to the token server. They correspond to the recognised keywords
228
+ arguments of the Python `requests <http://docs.python-requests.org/>`_ library
229
+ * :attr:`~eodag.config.PluginConfig.token_key` (``str``): The key pointing
230
+ to the token in the json response to the POST request to the token server
231
+ * :attr:`~eodag.config.PluginConfig.token_provision` (``str``) (**mandatory**): One of
232
+ ``qs`` or ``header``. This is how the token obtained will be used to authenticate the
233
+ user on protected requests. If ``qs`` is chosen, then ``token_qs_key`` is mandatory
234
+ * :attr:`~eodag.config.PluginConfig.login_form_xpath` (``str``) (**mandatory**): The
235
+ xpath to the HTML form element representing the user login form
236
+ * :attr:`~eodag.config.PluginConfig.authentication_uri_source` (``str``) (**mandatory**): Where
237
+ to look for the authentication_uri. One of ``config`` (in the configuration) or ``login-form``
238
+ (use the 'action' URL found in the login form retrieved with login_form_xpath). If the
239
+ value is ``config``, authentication_uri config param is mandatory
240
+ * :attr:`~eodag.config.PluginConfig.authentication_uri` (``str``): (**mandatory if
241
+ authentication_uri_source=config**) The URL of the authentication backend of the OIDC provider
242
+ * :attr:`~eodag.config.PluginConfig.user_consent_form_xpath` (``str``): The xpath to
243
+ the user consent form. The form is searched in the content of the response to the authorization request
244
+ * :attr:`~eodag.config.PluginConfig.user_consent_form_data` (``Dict[str, str]``): The data that
245
+ will be passed with the POST request on the form 'action' URL. The data are given as
246
+ key value pairs, the keys representing the data key and the value being either a 'constant'
247
+ string value, or a string of the form 'xpath(<path-to-a-value-to-be-retrieved>)' and representing a
248
+ value to be retrieved in the user consent form. The xpath must resolve directly to a
249
+ string value, not to an HTML element. Example: ``xpath(//input[@name="sessionDataKeyConsent"]/@value)``
250
+ * :attr:`~eodag.config.PluginConfig.additional_login_form_data` (``Dict[str, str]``): A mapping
251
+ giving additional data to be passed to the login POST request. The value follows
252
+ the same rules as with user_consent_form_data
253
+ * :attr:`~eodag.config.PluginConfig.exchange_url_error_pattern` (``Dict[str, str]``): Key/value
254
+ pairs of patterns/messages. If exchange_url contains the given pattern, the associated
255
+ message will be sent in an AuthenticationError
256
+ * :attr:`~eodag.config.PluginConfig.client_secret` (``str``): The OIDC provider's client
257
+ secret of the eodag provider
258
+ * :attr:`~eodag.config.PluginConfig.token_exchange_params` (``Dict[str, str]``): mandatory
259
+ keys for the dict: redirect_uri, client_id; A mapping between OIDC url query string
260
+ and token handler query string params (only necessary if they are not the same as for OIDC).
261
+ This is eodag provider dependant
262
+ * :attr:`~eodag.config.PluginConfig.token_qs_key` (``str``): (mandatory when token_provision=qs)
263
+ Refers to the name of the query param to be used in the query request
264
+ * :attr:`~eodag.config.PluginConfig.refresh_token_key` (``str``): The key pointing to
265
+ the refresh_token in the json response to the POST request to the token server
216
266
 
217
- # (mandatory) The xpath to the HTML form element representing the user login form
218
- login_form_xpath:
219
-
220
- # (mandatory) Where to look for the authentication_uri. One of 'config' (in the configuration) or 'login-form'
221
- # (use the 'action' URL found in the login form retrieved with login_form_xpath). If the value is 'config',
222
- # authentication_uri config param is mandatory
223
- authentication_uri_source:
224
-
225
- # (optional) The URL of the authentication backend of the OIDC provider
226
- authentication_uri:
227
-
228
- # (optional) The xpath to the user consent form. The form is searched in the content of the response
229
- # to the authorization request
230
- user_consent_form_xpath:
231
-
232
- # (optional) The data that will be passed with the POST request on the form 'action' URL. The data are
233
- # given as a key value pairs, the keys representing the data key and the value being either
234
- # a 'constant' string value, or a string of the form 'xpath(<path-to-a-value-to-be-retrieved>)'
235
- # and representing a value to be retrieved in the user consent form. The xpath must resolve
236
- # directly to a string value, not to an HTML element. Example:
237
- # `xpath(//input[@name="sessionDataKeyConsent"]/@value)`
238
- user_consent_form_data:
239
-
240
- # (optional) A mapping giving additional data to be passed to the login POST request. The value follows the
241
- # same rules as with user_consent_form_data
242
- additional_login_form_data:
243
-
244
- # (optional) Key/value pairs of patterns/messages. If exchange_url contains the given pattern, the associated
245
- message will be sent in an AuthenticationError
246
- exchange_url_error_pattern:
247
-
248
- # (optional) The OIDC provider's client secret of the eodag provider
249
- client_secret:
250
-
251
- # (optional) A mapping between OIDC url query string and token handler query string
252
- # params (only necessary if they are not the same as for OIDC). This is eodag provider
253
- # dependant
254
- token_exchange_params:
255
- redirect_uri:
256
- client_id:
257
-
258
- # (optional) Only necessary when 'token_provision' is 'qs'. Refers to the name of the query param to be
259
- # used in the query request
260
- token_qs_key:
261
-
262
- # (optional) The key pointing to the refresh_token in the json response to the POST request to the token server
263
- refresh_token_key:
264
267
  """
265
268
 
266
269
  SCOPE = "openid"
@@ -287,17 +290,17 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
287
290
 
288
291
  def authenticate(self) -> CodeAuthorizedAuth:
289
292
  """Authenticate"""
290
- self.token_info["access_token"] = self._get_access_token()
293
+ self._get_access_token()
291
294
 
292
295
  return CodeAuthorizedAuth(
293
- self.token_info["access_token"],
296
+ self.access_token,
294
297
  self.config.token_provision,
295
298
  key=getattr(self.config, "token_qs_key", None),
296
299
  )
297
300
 
298
301
  def _request_new_token(self) -> Dict[str, str]:
299
- """Fetch the access token with a new authentcation"""
300
- logger.debug("Fetching access token from %s", self.config.token_uri)
302
+ """Fetch the access token with a new authentication"""
303
+ logger.debug("Fetching access token from %s", self.token_endpoint)
301
304
  state = self.compute_state()
302
305
  authentication_response = self.authenticate_user(state)
303
306
  exchange_url = authentication_response.url
@@ -326,20 +329,22 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
326
329
  def _get_token_with_refresh_token(self) -> Dict[str, str]:
327
330
  """Fetch the access token with the refresh token"""
328
331
  logger.debug(
329
- "Fetching access token with refresh token from %s", self.config.token_uri
332
+ "Fetching access token with refresh token from %s.", self.token_endpoint
330
333
  )
331
334
  token_data: Dict[str, Any] = {
332
- "refresh_token": self.token_info["refresh_token"],
335
+ "refresh_token": self.refresh_token,
333
336
  "grant_type": "refresh_token",
334
337
  }
335
338
  token_data = self._prepare_token_post_data(token_data)
336
339
  post_request_kwargs: Any = {
337
340
  self.config.token_exchange_post_data_method: token_data
338
341
  }
342
+ ssl_verify = getattr(self.config, "ssl_verify", True)
339
343
  try:
340
344
  token_response = self.session.post(
341
- self.config.token_uri,
345
+ self.token_endpoint,
342
346
  timeout=HTTP_REQ_TIMEOUT,
347
+ verify=ssl_verify,
343
348
  **post_request_kwargs,
344
349
  )
345
350
  token_response.raise_for_status()
@@ -363,11 +368,13 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
363
368
  "state": state,
364
369
  "redirect_uri": self.config.redirect_uri,
365
370
  }
371
+ ssl_verify = getattr(self.config, "ssl_verify", True)
366
372
  authorization_response = self.session.get(
367
- self.config.authorization_uri,
373
+ self.authorization_endpoint,
368
374
  params=params,
369
375
  headers=USER_AGENT,
370
376
  timeout=HTTP_REQ_TIMEOUT,
377
+ verify=ssl_verify,
371
378
  )
372
379
 
373
380
  login_document = etree.HTML(authorization_response.text)
@@ -392,7 +399,7 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
392
399
  self.config.login_form_xpath.rstrip("/") + "/@action"
393
400
  )
394
401
  if not auth_uri or not auth_uri[0]:
395
- raise RequestError(
402
+ raise MisconfiguredError(
396
403
  f"Could not get auth_uri from {self.config.login_form_xpath}"
397
404
  )
398
405
  auth_uri = auth_uri[0]
@@ -401,7 +408,11 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
401
408
  if not auth_uri:
402
409
  raise MisconfiguredError("authentication_uri is missing")
403
410
  return self.session.post(
404
- auth_uri, data=login_data, headers=USER_AGENT, timeout=HTTP_REQ_TIMEOUT
411
+ auth_uri,
412
+ data=login_data,
413
+ headers=USER_AGENT,
414
+ timeout=HTTP_REQ_TIMEOUT,
415
+ verify=ssl_verify,
405
416
  )
406
417
 
407
418
  def grant_user_consent(self, authentication_response: Response) -> Response:
@@ -415,11 +426,13 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
415
426
  key: self._constant_or_xpath_extracted(value, user_consent_form)
416
427
  for key, value in self.config.user_consent_form_data.items()
417
428
  }
429
+ ssl_verify = getattr(self.config, "ssl_verify", True)
418
430
  return self.session.post(
419
- self.config.authorization_uri,
431
+ self.authorization_endpoint,
420
432
  data=user_consent_data,
421
433
  headers=USER_AGENT,
422
434
  timeout=HTTP_REQ_TIMEOUT,
435
+ verify=ssl_verify,
423
436
  )
424
437
 
425
438
  def _prepare_token_post_data(self, token_data: Dict[str, Any]) -> Dict[str, Any]:
@@ -467,10 +480,12 @@ class OIDCAuthorizationCodeFlowAuth(OIDCRefreshTokenBase):
467
480
  post_request_kwargs: Any = {
468
481
  self.config.token_exchange_post_data_method: token_exchange_data
469
482
  }
483
+ ssl_verify = getattr(self.config, "ssl_verify", True)
470
484
  r = self.session.post(
471
- self.config.token_uri,
485
+ self.token_endpoint,
472
486
  headers=USER_AGENT,
473
487
  timeout=HTTP_REQ_TIMEOUT,
488
+ verify=ssl_verify,
474
489
  **post_request_kwargs,
475
490
  )
476
491
  return r
@@ -36,9 +36,17 @@ class HttpQueryStringAuth(Authentication):
36
36
  """An Authentication plugin using HTTP query string parameters.
37
37
 
38
38
  This plugin sends credentials as query-string parameters.
39
+
40
+ :param provider: provider name
41
+ :param config: Authentication plugin configuration:
42
+
43
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): HttpQueryStringAuth
44
+ * :attr:`~eodag.config.PluginConfig.auth_uri` (``str``): used to check the credentials
45
+ given in the configuration
46
+
39
47
  Using :class:`~eodag.plugins.download.http.HTTPDownload` a download link
40
- `http://example.com?foo=bar` will become
41
- `http://example.com?foo=bar&apikey=XXX&otherkey=YYY` if associated to the following
48
+ ``http://example.com?foo=bar`` will become
49
+ ``http://example.com?foo=bar&apikey=XXX&otherkey=YYY`` if associated to the following
42
50
  configuration::
43
51
 
44
52
  provider:
@@ -56,7 +64,7 @@ class HttpQueryStringAuth(Authentication):
56
64
  ...
57
65
  ...
58
66
 
59
- If `auth_uri` is specified (optional), it will be used to check credentials through
67
+ If ``auth_uri`` is specified (optional), it will be used to check credentials through
60
68
  :meth:`~eodag.plugins.authentication.query_string.HttpQueryStringAuth.authenticate`
61
69
  """
62
70
 
@@ -82,7 +90,7 @@ class HttpQueryStringAuth(Authentication):
82
90
  except requests.exceptions.Timeout as exc:
83
91
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
84
92
  except RequestException as e:
85
- raise AuthenticationError(f"Could no authenticate: {str(e)}")
93
+ raise AuthenticationError("Could no authenticate", str(e)) from e
86
94
 
87
95
  return auth
88
96
 
@@ -75,7 +75,7 @@ class RequestsSASAuth(AuthBase):
75
75
  except requests.exceptions.Timeout as exc:
76
76
  raise TimeOutError(exc, timeout=HTTP_REQ_TIMEOUT) from exc
77
77
  except (requests.RequestException, JSONDecodeError, KeyError) as e:
78
- raise AuthenticationError(f"Could no get signed url: {str(e)}")
78
+ raise AuthenticationError("Could no get signed url", str(e)) from e
79
79
  else:
80
80
  self.signed_urls[req_signed_url] = signed_url
81
81
 
@@ -85,7 +85,24 @@ class RequestsSASAuth(AuthBase):
85
85
 
86
86
 
87
87
  class SASAuth(Authentication):
88
- """SASAuth authentication plugin"""
88
+ """SASAuth authentication plugin
89
+
90
+ An apiKey that is added in the headers can be given in the credentials in the config file.
91
+
92
+ :param provider: provider name
93
+ :param config: Authentication plugin configuration:
94
+
95
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): SASAuth
96
+ * :attr:`~eodag.config.PluginConfig.auth_uri` (``str``) (**mandatory**): url used to
97
+ get the signed url
98
+ * :attr:`~eodag.config.PluginConfig.signed_url_key` (``str``) (**mandatory**): key to
99
+ get the signed url
100
+ * :attr:`~eodag.config.PluginConfig.headers` (``Dict[str, str]``) (**mandatory if
101
+ apiKey is used**): headers to be added to the requests
102
+ * :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates should be
103
+ verified in the requests; default: ``True``
104
+
105
+ """
89
106
 
90
107
  def validate_config_credentials(self) -> None:
91
108
  """Validate configured credentials"""