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
@@ -24,9 +24,10 @@ from eodag.plugins.search.base import Search
24
24
  class Api(Search, Download):
25
25
  """Plugins API Base plugin
26
26
 
27
- An Api plugin inherit the methods from Search and Download plugins.
27
+ An Api plugin inherits the methods from Search and Download plugins.
28
28
 
29
29
  There are three methods that it must implement:
30
+
30
31
  - ``query``: search for products
31
32
  - ``download``: download a single :class:`~eodag.api.product._product.EOProduct`
32
33
  - ``download_all``: download multiple products from a :class:`~eodag.api.search_result.SearchResult`
@@ -35,14 +36,14 @@ class Api(Search, Download):
35
36
 
36
37
  - download data in the ``output_dir`` folder defined in the plugin's
37
38
  configuration or passed through kwargs
38
- - extract products from their archive (if relevant) if ``extract`` is set to True
39
- (True by default)
39
+ - extract products from their archive (if relevant) if ``extract`` is set to ``True``
40
+ (``True`` by default)
40
41
  - save a product in an archive/directory (in ``output_dir``) whose name must be
41
42
  the product's ``title`` property
42
43
  - update the product's ``location`` attribute once its data is downloaded (and
43
44
  eventually after it's extracted) to the product's location given as a file URI
44
45
  (e.g. 'file:///tmp/product_folder' on Linux or
45
- 'file:///C:/Users/username/AppData/LOcal/Temp' on Windows)
46
+ 'file:///C:/Users/username/AppData/Local/Temp' on Windows)
46
47
  - save a *record* file in the directory ``output_dir/.downloaded`` whose name
47
48
  is built on the MD5 hash of the product's ``product_type`` and ``properties['id']``
48
49
  attributes (``hashlib.md5((product.product_type+"-"+product.properties['id']).encode("utf-8")).hexdigest()``)
@@ -52,4 +53,9 @@ class Api(Search, Download):
52
53
  - not try to download a product if its *record* file exists as long as the expected
53
54
  product's file/directory. If the *record* file only is found, it must be deleted
54
55
  (it certainly indicates that the download didn't complete)
56
+
57
+ :param provider: An EODAG provider name
58
+ :type provider: str
59
+ :param config: An EODAG plugin configuration
60
+ :type config: Dict[str, Any]
55
61
  """
@@ -20,16 +20,21 @@ from __future__ import annotations
20
20
  import logging
21
21
  import os
22
22
  from datetime import datetime, timezone
23
- from typing import TYPE_CHECKING
23
+ from typing import TYPE_CHECKING, Annotated
24
24
 
25
25
  import geojson
26
26
  from ecmwfapi import ECMWFDataServer, ECMWFService
27
27
  from ecmwfapi.api import APIException, Connection, get_apikey_values
28
+ from pydantic.fields import FieldInfo
28
29
 
29
30
  from eodag.plugins.apis.base import Api
30
31
  from eodag.plugins.search import PreparedSearch
31
32
  from eodag.plugins.search.base import Search
32
- from eodag.plugins.search.build_search_result import BuildPostSearchResult
33
+ from eodag.plugins.search.build_search_result import (
34
+ ECMWF_KEYWORDS,
35
+ ECMWFSearch,
36
+ keywords_to_mdt,
37
+ )
33
38
  from eodag.utils import (
34
39
  DEFAULT_DOWNLOAD_TIMEOUT,
35
40
  DEFAULT_DOWNLOAD_WAIT,
@@ -58,7 +63,7 @@ logger = logging.getLogger("eodag.apis.ecmwf")
58
63
  ECMWF_MARS_KNOWN_FORMATS = {"grib": "grib", "netcdf": "nc"}
59
64
 
60
65
 
61
- class EcmwfApi(Api, BuildPostSearchResult):
66
+ class EcmwfApi(Api, ECMWFSearch):
62
67
  """A plugin that enables to build download-request and download data on ECMWF MARS.
63
68
 
64
69
  Builds a single ready-to-download :class:`~eodag.api.product._product.EOProduct`
@@ -68,20 +73,36 @@ class EcmwfApi(Api, BuildPostSearchResult):
68
73
  is in query), or on MARS Operational Archive (if ``dataset`` parameter is not in
69
74
  query).
70
75
 
71
- This class inherits from :class:`~eodag.plugins.apis.base.Api` for compatibility,
72
- :class:`~eodag.plugins.download.base.Download` for download methods, and
73
- :class:`~eodag.plugins.search.qssearch.QueryStringSearch` for metadata-mapping and
74
- query build methods.
76
+ This class inherits from :class:`~eodag.plugins.apis.base.Api` for compatibility and
77
+ :class:`~eodag.plugins.search.build_search_result.ECMWFSearch` for the creation
78
+ of the search result.
79
+
80
+ :param provider: provider name
81
+ :param config: Api plugin configuration:
82
+
83
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): EcmwfApi
84
+ * :attr:`~eodag.config.PluginConfig.auth_endpoint` (``str``) (**mandatory**): url of
85
+ the authentication endpoint of the ecmwf api
86
+ * :attr:`~eodag.config.PluginConfig.metadata_mapping` (``Dict[str, Union[str, list]]``): how
87
+ parameters should be mapped between the provider and eodag; If a string is given, this is
88
+ the mapping parameter returned by provider -> eodag parameter. If a list with 2 elements
89
+ is given, the first one is the mapping eodag parameter -> provider query parameters
90
+ and the second one the mapping provider result parameter -> eodag parameter
75
91
  """
76
92
 
77
93
  def __init__(self, provider: str, config: PluginConfig) -> None:
78
94
  # init self.config.metadata_mapping using Search Base plugin
95
+ config.metadata_mapping = {
96
+ **keywords_to_mdt(ECMWF_KEYWORDS, "ecmwf"),
97
+ **config.metadata_mapping,
98
+ }
79
99
  Search.__init__(self, provider, config)
80
100
 
81
101
  # needed by QueryStringSearch.build_query_string / format_free_text_search
82
102
  self.config.__dict__.setdefault("free_text_search_operations", {})
83
103
  # needed for compatibility
84
104
  self.config.__dict__.setdefault("pagination", {"next_page_query_obj": "{{}}"})
105
+ self.config.__dict__.setdefault("api_endpoint", "")
85
106
 
86
107
  def do_search(self, *args: Any, **kwargs: Any) -> List[Dict[str, Any]]:
87
108
  """Should perform the actual search request."""
@@ -98,9 +119,9 @@ class EcmwfApi(Api, BuildPostSearchResult):
98
119
  # productType
99
120
  if not kwargs.get("productType"):
100
121
  kwargs["productType"] = "%s_%s_%s" % (
101
- kwargs.get("dataset", "mars"),
102
- kwargs.get("type", ""),
103
- kwargs.get("levtype", ""),
122
+ kwargs.get("ecmwf:dataset", "mars"),
123
+ kwargs.get("ecmwf:type", ""),
124
+ kwargs.get("ecmwf:levtype", ""),
104
125
  )
105
126
  # start date
106
127
  if "startTimeFromAscendingNode" not in kwargs:
@@ -122,7 +143,7 @@ class EcmwfApi(Api, BuildPostSearchResult):
122
143
  if "geometry" in kwargs:
123
144
  kwargs["geometry"] = get_geometry_from_various(geometry=kwargs["geometry"])
124
145
 
125
- return BuildPostSearchResult.query(self, prep, **kwargs)
146
+ return ECMWFSearch.query(self, prep, **kwargs)
126
147
 
127
148
  def authenticate(self) -> Dict[str, Optional[str]]:
128
149
  """Check credentials and returns information needed for auth
@@ -133,7 +154,7 @@ class EcmwfApi(Api, BuildPostSearchResult):
133
154
  # Get credentials from eodag or using ecmwf conf
134
155
  email = getattr(self.config, "credentials", {}).get("username", None)
135
156
  key = getattr(self.config, "credentials", {}).get("password", None)
136
- url = getattr(self.config, "api_endpoint", None)
157
+ url = getattr(self.config, "auth_endpoint", None)
137
158
  if not all([email, key, url]):
138
159
  key, url, email = get_apikey_values()
139
160
 
@@ -157,8 +178,8 @@ class EcmwfApi(Api, BuildPostSearchResult):
157
178
  product: EOProduct,
158
179
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
159
180
  progress_callback: Optional[ProgressCallback] = None,
160
- wait: int = DEFAULT_DOWNLOAD_WAIT,
161
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
181
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
182
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
162
183
  **kwargs: Unpack[DownloadConf],
163
184
  ) -> Optional[str]:
164
185
  """Download data from ECMWF MARS"""
@@ -247,8 +268,8 @@ class EcmwfApi(Api, BuildPostSearchResult):
247
268
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
248
269
  downloaded_callback: Optional[DownloadedCallback] = None,
249
270
  progress_callback: Optional[ProgressCallback] = None,
250
- wait: int = DEFAULT_DOWNLOAD_WAIT,
251
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
271
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
272
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
252
273
  **kwargs: Unpack[DownloadConf],
253
274
  ) -> List[str]:
254
275
  """
@@ -267,3 +288,15 @@ class EcmwfApi(Api, BuildPostSearchResult):
267
288
  def clear(self) -> None:
268
289
  """Clear search context"""
269
290
  pass
291
+
292
+ def discover_queryables(
293
+ self, **kwargs: Any
294
+ ) -> Optional[Dict[str, Annotated[Any, FieldInfo]]]:
295
+ """Fetch queryables list from provider using metadata mapping
296
+
297
+ :param kwargs: additional filters for queryables (`productType` and other search
298
+ arguments)
299
+ :returns: fetched queryable parameters dict
300
+ """
301
+ product_type = kwargs.get("productType", None)
302
+ return self.queryables_from_metadata_mapping(product_type)
@@ -68,7 +68,30 @@ logger = logging.getLogger("eodag.apis.usgs")
68
68
 
69
69
 
70
70
  class UsgsApi(Api):
71
- """A plugin that enables to query and download data on the USGS catalogues"""
71
+ """A plugin that enables to query and download data on the USGS catalogues
72
+
73
+ :param provider: provider name
74
+ :param config: Api plugin configuration:
75
+
76
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): UsgsApi
77
+ * :attr:`~eodag.config.PluginConfig.pagination` (:class:`~eodag.config.PluginConfig.Pagination`)
78
+ (**mandatory**): object containing parameters for pagination; should contain the attribute
79
+ :attr:`~eodag.config.PluginConfig.Pagination.total_items_nb_key_path`
80
+ which is indicating the key for the number of total items in the provider result
81
+ * :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates
82
+ should be verified in the download request; default: ``True``
83
+ * :attr:`~eodag.config.PluginConfig.need_auth` (``bool``): if authentication is required
84
+ for search; default: ``False``
85
+ * :attr:`~eodag.config.PluginConfig.extract` (``bool``): if the content of the downloaded
86
+ file should be extracted; default: ``True``
87
+ * :attr:`~eodag.config.PluginConfig.order_enabled` (``bool``): if the product has to
88
+ be ordered to download it; default: ``False``
89
+ * :attr:`~eodag.config.PluginConfig.metadata_mapping` (``Dict[str, Union[str, list]]``): how
90
+ parameters should be mapped between the provider and eodag; If a string is given, this is
91
+ the mapping parameter returned by provider -> eodag parameter. If a list with 2 elements
92
+ is given, the first one is the mapping eodag parameter -> provider query parameters
93
+ and the second one the mapping provider result parameter -> eodag parameter
94
+ """
72
95
 
73
96
  def __init__(self, provider: str, config: PluginConfig) -> None:
74
97
  super(UsgsApi, self).__init__(provider, config)
@@ -256,7 +279,7 @@ class UsgsApi(Api):
256
279
  f"Product type {usgs_dataset} may not exist on USGS EE catalog"
257
280
  )
258
281
  api.logout()
259
- raise RequestError(e)
282
+ raise RequestError.from_error(e) from e
260
283
 
261
284
  api.logout()
262
285
 
@@ -274,8 +297,8 @@ class UsgsApi(Api):
274
297
  product: EOProduct,
275
298
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
276
299
  progress_callback: Optional[ProgressCallback] = None,
277
- wait: int = DEFAULT_DOWNLOAD_WAIT,
278
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
300
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
301
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
279
302
  **kwargs: Unpack[DownloadConf],
280
303
  ) -> Optional[str]:
281
304
  """Download data from USGS catalogues"""
@@ -352,7 +375,7 @@ class UsgsApi(Api):
352
375
  logger.debug(f"Downloading {req_url}")
353
376
  ssl_verify = getattr(self.config, "ssl_verify", True)
354
377
 
355
- @self._download_retry(product, wait, timeout)
378
+ @self._order_download_retry(product, wait, timeout)
356
379
  def download_request(
357
380
  product: EOProduct,
358
381
  fs_path: str,
@@ -444,8 +467,8 @@ class UsgsApi(Api):
444
467
  auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
445
468
  downloaded_callback: Optional[DownloadedCallback] = None,
446
469
  progress_callback: Optional[ProgressCallback] = None,
447
- wait: int = DEFAULT_DOWNLOAD_WAIT,
448
- timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
470
+ wait: float = DEFAULT_DOWNLOAD_WAIT,
471
+ timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
449
472
  **kwargs: Unpack[DownloadConf],
450
473
  ) -> List[str]:
451
474
  """
@@ -30,14 +30,23 @@ if TYPE_CHECKING:
30
30
  class AwsAuth(Authentication):
31
31
  """AWS authentication plugin
32
32
 
33
- Authentication will use the first valid method within the following ones:
33
+ Authentication will use the first valid method within the following ones depending on which
34
+ parameters are available in the configuration:
34
35
 
35
- - auth anonymously using no-sign-request
36
- - auth using ``aws_profile``
37
- - auth using ``aws_access_key_id`` and ``aws_secret_access_key``
36
+ * auth anonymously using no-sign-request
37
+ * auth using ``aws_profile``
38
+ * auth using ``aws_access_key_id`` and ``aws_secret_access_key``
38
39
  (optionally ``aws_session_token``)
39
- - auth using current environment (AWS environment variables and/or ``~/aws/*``),
40
+ * auth using current environment (AWS environment variables and/or ``~/aws/*``),
40
41
  will be skipped if AWS credentials are filled in eodag conf
42
+
43
+ :param provider: provider name
44
+ :param config: Authentication plugin configuration:
45
+
46
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): AwsAuth
47
+ * :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``) (mandatory for ``creodias_s3``):
48
+ which error code is returned in case of an authentication error
49
+
41
50
  """
42
51
 
43
52
  s3_client: S3Client
@@ -27,7 +27,16 @@ if TYPE_CHECKING:
27
27
 
28
28
 
29
29
  class Authentication(PluginTopic):
30
- """Plugins authentication Base plugin"""
30
+ """Plugins authentication Base plugin
31
+
32
+ :param provider: provider name
33
+ :param config: Authentication plugin configuration:
34
+
35
+ * :attr:`~eodag.config.PluginConfig.matching_url` (``str``): URL pattern to match with search plugin endpoint or
36
+ download link
37
+ * :attr:`~eodag.config.PluginConfig.matching_conf` (``Dict[str, Any]``): Part of the search or download plugin
38
+ configuration that needs authentication and helps identifying it
39
+ """
31
40
 
32
41
  def authenticate(self) -> Union[AuthBase, Dict[str, str]]:
33
42
  """Authenticate"""
@@ -29,7 +29,18 @@ if TYPE_CHECKING:
29
29
 
30
30
 
31
31
  class GenericAuth(Authentication):
32
- """GenericAuth authentication plugin"""
32
+ """GenericAuth authentication plugin (authentication using ``username`` and ``password``)
33
+
34
+ The mandatory parameters that have to be added in the eodag config are username and password.
35
+
36
+ :param provider: provider name
37
+ :param config: Authentication plugin configuration:
38
+
39
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): GenericAuth
40
+ * :attr:`~eodag.config.PluginConfig.method` (``str``): specifies if digest authentication
41
+ (``digest``) or basic authentication (``basic``) should be used; default: ``basic``
42
+
43
+ """
33
44
 
34
45
  def authenticate(self) -> AuthBase:
35
46
  """Authenticate"""
@@ -48,6 +59,6 @@ class GenericAuth(Authentication):
48
59
  )
49
60
  else:
50
61
  raise MisconfiguredError(
51
- f"Cannot authenticate with {self.provider}:",
52
- f"method {method} is not supported. Must be one of digest or basic",
62
+ f"Cannot authenticate with {self.provider}",
63
+ f"Method {method} is not supported; it must be one of 'digest' or 'basic'.",
53
64
  )
@@ -34,7 +34,14 @@ class HTTPHeaderAuth(Authentication):
34
34
  This plugin enables implementation of custom HTTP authentication scheme (other than Basic, Digest, Token
35
35
  negotiation et al.) using HTTP headers.
36
36
 
37
- The plugin is configured as follows in the providers config file::
37
+ :param provider: provider name
38
+ :param config: Authentication plugin configuration:
39
+
40
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): HTTPHeaderAuth
41
+ * :attr:`~eodag.config.PluginConfig.headers` (``Dict[str, str]``): dictionary containing
42
+ all keys/value pairs that should be added to the headers
43
+
44
+ Below an example for the configuration in the providers config file is shown::
38
45
 
39
46
  provider:
40
47
  ...
@@ -47,9 +54,9 @@ class HTTPHeaderAuth(Authentication):
47
54
  ...
48
55
  ...
49
56
 
50
- As you can see in the sample above, the maintainer of `provider` define the headers that will be used in the
51
- authentication process as-is, by giving their names (e.g. `Authorization`) and their value (e.g
52
- `"Something {userinput}"`) as regular Python string templates that enable passing in the user input necessary to
57
+ As you can see in the sample above, the maintainer of ``provider`` define the headers that will be used in the
58
+ authentication process as-is, by giving their names (e.g. ``Authorization``) and their value (e.g
59
+ ``"Something {userinput}"``) as regular Python string templates that enable passing in the user input necessary to
53
60
  compute its identity. The user input awaited in the header value string must be present in the user config file.
54
61
  In the sample above, the plugin await for user credentials to be specified as::
55
62
 
@@ -72,6 +79,7 @@ class HTTPHeaderAuth(Authentication):
72
79
  X-Another-Special-Header: "YYY"
73
80
  ...
74
81
  ...
82
+
75
83
  """
76
84
 
77
85
  def authenticate(self) -> HeaderAuth:
@@ -41,19 +41,38 @@ logger = logging.getLogger("eodag.auth.keycloak")
41
41
  class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
42
42
  """Authentication plugin using Keycloak and OpenId Connect.
43
43
 
44
- This plugin request a token and use it through a query-string or a header.
44
+ This plugin requests a token which is added to a query-string or a header for authentication.
45
+
46
+ :param provider: provider name
47
+ :param config: Authentication plugin configuration:
48
+
49
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): KeycloakOIDCPasswordAuth
50
+ * :attr:`~eodag.config.PluginConfig.oidc_config_url` (``str``) (**mandatory**):
51
+ The url to get the OIDC Provider's endpoints
52
+ * :attr:`~eodag.config.PluginConfig.client_id` (``str``) (**mandatory**): keycloak client id
53
+ * :attr:`~eodag.config.PluginConfig.client_secret` (``str``) (**mandatory**): keycloak
54
+ client secret, set to null if no secret is used
55
+ * :attr:`~eodag.config.PluginConfig.token_provision` (``str``) (**mandatory**): if the
56
+ token should be added to the query string (``qs``) or to the header (``header``)
57
+ * :attr:`~eodag.config.PluginConfig.token_qs_key` (``str``): (**mandatory if token_provision=qs**)
58
+ key of the param added to the query string
59
+ * :attr:`~eodag.config.PluginConfig.allowed_audiences` (``List[str]``) (**mandatory**):
60
+ The allowed audiences that have to be present in the user token.
61
+ * :attr:`~eodag.config.PluginConfig.auth_error_code` (``int``): which error code is
62
+ returned in case of an authentication error
63
+ * :attr:`~eodag.config.PluginConfig.ssl_verify` (``bool``): if the ssl certificates
64
+ should be verified in the token request; default: ``True``
45
65
 
46
66
  Using :class:`~eodag.plugins.download.http.HTTPDownload` a download link
47
- `http://example.com?foo=bar` will become
48
- `http://example.com?foo=bar&my-token=obtained-token` if associated to the following
67
+ ``http://example.com?foo=bar`` will become
68
+ ``http://example.com?foo=bar&my-token=obtained-token`` if associated to the following
49
69
  configuration::
50
70
 
51
71
  provider:
52
72
  ...
53
73
  auth:
54
74
  plugin: KeycloakOIDCPasswordAuth
55
- auth_base_uri: 'https://somewhere/auth'
56
- realm: 'the-realm'
75
+ oidc_config_url: 'https://somewhere/auth/realms/realm/.well-known/openid-configuration'
57
76
  client_id: 'SOME_ID'
58
77
  client_secret: '01234-56789'
59
78
  token_provision: qs
@@ -62,15 +81,14 @@ class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
62
81
  ...
63
82
 
64
83
  If configured to send the token through the header, the download request header will
65
- be updated with `Authorization: "Bearer obtained-token"` if associated to the
84
+ be updated with ``Authorization: "Bearer obtained-token"`` if associated to the
66
85
  following configuration::
67
86
 
68
87
  provider:
69
88
  ...
70
89
  auth:
71
90
  plugin: KeycloakOIDCPasswordAuth
72
- auth_base_uri: 'https://somewhere/auth'
73
- realm: 'the-realm'
91
+ oidc_config_url: 'https://somewhere/auth/realms/realm/.well-known/openid-configuration'
74
92
  client_id: 'SOME_ID'
75
93
  client_secret: '01234-56789'
76
94
  token_provision: header
@@ -79,8 +97,12 @@ class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
79
97
  """
80
98
 
81
99
  GRANT_TYPE = "password"
82
- TOKEN_URL_TEMPLATE = "{auth_base_uri}/realms/{realm}/protocol/openid-connect/token"
83
- REQUIRED_PARAMS = ["auth_base_uri", "client_id", "client_secret", "token_provision"]
100
+ REQUIRED_PARAMS = [
101
+ "oidc_config_url",
102
+ "client_id",
103
+ "client_secret",
104
+ "token_provision",
105
+ ]
84
106
 
85
107
  def __init__(self, provider: str, config: PluginConfig) -> None:
86
108
  super(KeycloakOIDCPasswordAuth, self).__init__(provider, config)
@@ -101,10 +123,9 @@ class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
101
123
  Makes authentication request
102
124
  """
103
125
  self.validate_config_credentials()
104
- access_token = self._get_access_token()
105
- self.token_info["access_token"] = access_token
126
+ self._get_access_token()
106
127
  return CodeAuthorizedAuth(
107
- self.token_info["access_token"],
128
+ self.access_token,
108
129
  self.config.token_provision,
109
130
  key=getattr(self.config, "token_qs_key", None),
110
131
  )
@@ -117,15 +138,14 @@ class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
117
138
  "grant_type": self.GRANT_TYPE,
118
139
  }
119
140
  credentials = {k: v for k, v in self.config.credentials.items()}
141
+ ssl_verify = getattr(self.config, "ssl_verify", True)
120
142
  try:
121
143
  response = self.session.post(
122
- self.TOKEN_URL_TEMPLATE.format(
123
- auth_base_uri=self.config.auth_base_uri.rstrip("/"),
124
- realm=self.config.realm,
125
- ),
144
+ self.token_endpoint,
126
145
  data=dict(req_data, **credentials),
127
146
  headers=USER_AGENT,
128
147
  timeout=HTTP_REQ_TIMEOUT,
148
+ verify=ssl_verify,
129
149
  )
130
150
  response.raise_for_status()
131
151
  except requests.exceptions.Timeout as exc:
@@ -140,17 +160,16 @@ class KeycloakOIDCPasswordAuth(OIDCRefreshTokenBase):
140
160
  "client_id": self.config.client_id,
141
161
  "client_secret": self.config.client_secret,
142
162
  "grant_type": "refresh_token",
143
- "refresh_token": self.token_info["refresh_token"],
163
+ "refresh_token": self.refresh_token,
144
164
  }
165
+ ssl_verify = getattr(self.config, "ssl_verify", True)
145
166
  try:
146
167
  response = self.session.post(
147
- self.TOKEN_URL_TEMPLATE.format(
148
- auth_base_uri=self.config.auth_base_uri.rstrip("/"),
149
- realm=self.config.realm,
150
- ),
168
+ self.token_endpoint,
151
169
  data=req_data,
152
170
  headers=USER_AGENT,
153
171
  timeout=HTTP_REQ_TIMEOUT,
172
+ verify=ssl_verify,
154
173
  )
155
174
  response.raise_for_status()
156
175
  except requests.RequestException as e:
@@ -26,7 +26,17 @@ if TYPE_CHECKING:
26
26
 
27
27
 
28
28
  class OAuth(Authentication):
29
- """OAuth authentication plugin"""
29
+ """OAuth authentication plugin
30
+
31
+ The mandatory parameters that have to be added in the eodag config are ``aws_access_key_id``
32
+ and ``aws_secret_access_key``.
33
+
34
+ :param provider: provider name
35
+ :param config: Authentication plugin configuration:
36
+
37
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): OAuth
38
+
39
+ """
30
40
 
31
41
  def __init__(self, provider: str, config: PluginConfig) -> None:
32
42
  super(OAuth, self).__init__(provider, config)