eodag 3.0.0b2__py3-none-any.whl → 3.0.1__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 (84) hide show
  1. eodag/__init__.py +6 -8
  2. eodag/api/core.py +295 -287
  3. eodag/api/product/__init__.py +10 -4
  4. eodag/api/product/_assets.py +2 -14
  5. eodag/api/product/_product.py +16 -30
  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 +12 -31
  9. eodag/api/search_result.py +33 -12
  10. eodag/cli.py +35 -19
  11. eodag/config.py +455 -155
  12. eodag/plugins/apis/base.py +13 -7
  13. eodag/plugins/apis/ecmwf.py +16 -7
  14. eodag/plugins/apis/usgs.py +68 -16
  15. eodag/plugins/authentication/aws_auth.py +25 -7
  16. eodag/plugins/authentication/base.py +10 -1
  17. eodag/plugins/authentication/generic.py +14 -3
  18. eodag/plugins/authentication/header.py +12 -4
  19. eodag/plugins/authentication/keycloak.py +41 -22
  20. eodag/plugins/authentication/oauth.py +11 -1
  21. eodag/plugins/authentication/openid_connect.py +183 -167
  22. eodag/plugins/authentication/qsauth.py +12 -4
  23. eodag/plugins/authentication/sas_auth.py +19 -2
  24. eodag/plugins/authentication/token.py +59 -11
  25. eodag/plugins/authentication/token_exchange.py +19 -19
  26. eodag/plugins/crunch/base.py +7 -2
  27. eodag/plugins/crunch/filter_date.py +8 -11
  28. eodag/plugins/crunch/filter_latest_intersect.py +5 -7
  29. eodag/plugins/crunch/filter_latest_tpl_name.py +2 -5
  30. eodag/plugins/crunch/filter_overlap.py +9 -15
  31. eodag/plugins/crunch/filter_property.py +9 -14
  32. eodag/plugins/download/aws.py +84 -99
  33. eodag/plugins/download/base.py +36 -77
  34. eodag/plugins/download/creodias_s3.py +11 -2
  35. eodag/plugins/download/http.py +134 -109
  36. eodag/plugins/download/s3rest.py +37 -43
  37. eodag/plugins/manager.py +173 -41
  38. eodag/plugins/search/__init__.py +9 -9
  39. eodag/plugins/search/base.py +35 -35
  40. eodag/plugins/search/build_search_result.py +55 -64
  41. eodag/plugins/search/cop_marine.py +113 -32
  42. eodag/plugins/search/creodias_s3.py +20 -8
  43. eodag/plugins/search/csw.py +41 -1
  44. eodag/plugins/search/data_request_search.py +119 -14
  45. eodag/plugins/search/qssearch.py +619 -197
  46. eodag/plugins/search/static_stac_search.py +25 -23
  47. eodag/resources/ext_product_types.json +1 -1
  48. eodag/resources/product_types.yml +211 -56
  49. eodag/resources/providers.yml +1762 -1809
  50. eodag/resources/stac.yml +3 -163
  51. eodag/resources/user_conf_template.yml +134 -119
  52. eodag/rest/config.py +1 -2
  53. eodag/rest/constants.py +0 -1
  54. eodag/rest/core.py +70 -92
  55. eodag/rest/errors.py +181 -0
  56. eodag/rest/server.py +24 -330
  57. eodag/rest/stac.py +105 -630
  58. eodag/rest/types/eodag_search.py +17 -15
  59. eodag/rest/types/queryables.py +5 -14
  60. eodag/rest/types/stac_search.py +18 -13
  61. eodag/rest/utils/rfc3339.py +0 -1
  62. eodag/types/__init__.py +24 -6
  63. eodag/types/download_args.py +14 -5
  64. eodag/types/queryables.py +1 -2
  65. eodag/types/search_args.py +10 -11
  66. eodag/types/whoosh.py +0 -2
  67. eodag/utils/__init__.py +97 -136
  68. eodag/utils/constraints.py +0 -8
  69. eodag/utils/exceptions.py +23 -9
  70. eodag/utils/import_system.py +0 -4
  71. eodag/utils/logging.py +37 -80
  72. eodag/utils/notebook.py +4 -4
  73. eodag/utils/requests.py +13 -23
  74. eodag/utils/rest.py +0 -4
  75. eodag/utils/stac_reader.py +3 -15
  76. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/METADATA +41 -24
  77. eodag-3.0.1.dist-info/RECORD +109 -0
  78. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/WHEEL +1 -1
  79. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/entry_points.txt +1 -0
  80. eodag/resources/constraints/climate-dt.json +0 -13
  81. eodag/resources/constraints/extremes-dt.json +0 -8
  82. eodag-3.0.0b2.dist-info/RECORD +0 -110
  83. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/LICENSE +0 -0
  84. {eodag-3.0.0b2.dist-info → eodag-3.0.1.dist-info}/top_level.txt +0 -0
@@ -24,26 +24,27 @@ 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`
33
34
 
34
35
  The download methods must:
35
36
 
36
- - download data in the ``outputs_prefix`` folder defined in the plugin's
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)
40
- - save a product in an archive/directory (in ``outputs_prefix``) whose name must be
39
+ - extract products from their archive (if relevant) if ``extract`` is set to ``True``
40
+ (``True`` by default)
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
- - save a *record* file in the directory ``outputs_prefix/.downloaded`` whose name
46
+ 'file:///C:/Users/username/AppData/Local/Temp' on Windows)
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()``)
49
50
  and whose content is the product's ``remote_location`` attribute itself.
@@ -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
  """
@@ -68,10 +68,20 @@ class EcmwfApi(Api, BuildPostSearchResult):
68
68
  is in query), or on MARS Operational Archive (if ``dataset`` parameter is not in
69
69
  query).
70
70
 
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.
71
+ This class inherits from :class:`~eodag.plugins.apis.base.Api` for compatibility and
72
+ :class:`~eodag.plugins.search.build_search_result.BuildPostSearchResult` for the creation
73
+ of the search result.
74
+
75
+ :param provider: provider name
76
+ :param config: Api plugin configuration:
77
+
78
+ * :attr:`~eodag.config.PluginConfig.type` (``str``) (**mandatory**): EcmwfApi
79
+ * :attr:`~eodag.config.PluginConfig.api_endpoint` (``str``) (**mandatory**): url of the ecmwf api
80
+ * :attr:`~eodag.config.PluginConfig.metadata_mapping` (``Dict[str, Union[str, list]]``): how
81
+ parameters should be mapped between the provider and eodag; If a string is given, this is
82
+ the mapping parameter returned by provider -> eodag parameter. If a list with 2 elements
83
+ is given, the first one is the mapping eodag parameter -> provider query parameters
84
+ and the second one the mapping provider result parameter -> eodag parameter
75
85
  """
76
86
 
77
87
  def __init__(self, provider: str, config: PluginConfig) -> None:
@@ -128,7 +138,6 @@ class EcmwfApi(Api, BuildPostSearchResult):
128
138
  """Check credentials and returns information needed for auth
129
139
 
130
140
  :returns: {key, url, email} dictionary
131
- :rtype: dict
132
141
  :raises: :class:`~eodag.utils.exceptions.AuthenticationError`
133
142
  """
134
143
  # Get credentials from eodag or using ecmwf conf
@@ -165,8 +174,8 @@ class EcmwfApi(Api, BuildPostSearchResult):
165
174
  """Download data from ECMWF MARS"""
166
175
  product_format = product.properties.get("format", "grib")
167
176
  product_extension = ECMWF_MARS_KNOWN_FORMATS.get(product_format, product_format)
168
- kwargs["outputs_extension"] = kwargs.get(
169
- "outputs_extension", f".{product_extension}"
177
+ kwargs["output_extension"] = kwargs.get(
178
+ "output_extension", f".{product_extension}"
170
179
  )
171
180
 
172
181
  # Prepare download
@@ -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)
@@ -128,13 +151,14 @@ class UsgsApi(Api):
128
151
  raise NoMatchingProductType(
129
152
  "Cannot search on USGS without productType specified"
130
153
  )
131
- if kwargs.get("sortBy"):
154
+ if kwargs.get("sort_by"):
132
155
  raise ValidationError("USGS does not support sorting feature")
133
156
 
134
157
  self.authenticate()
135
158
 
136
159
  product_type_def_params = self.config.products.get( # type: ignore
137
- product_type, self.config.products[GENERIC_PRODUCT_TYPE] # type: ignore
160
+ product_type,
161
+ self.config.products[GENERIC_PRODUCT_TYPE], # type: ignore
138
162
  )
139
163
  usgs_dataset = format_dict_items(product_type_def_params, **kwargs)["dataset"]
140
164
  start_date = kwargs.pop("startTimeFromAscendingNode", None)
@@ -172,11 +196,39 @@ class UsgsApi(Api):
172
196
  max_results=items_per_page,
173
197
  starting_number=(1 + (page - 1) * items_per_page),
174
198
  )
175
- logger.info(
176
- f"Sending search request for {usgs_dataset} with {api_search_kwargs}"
177
- )
178
199
 
179
- results = api.scene_search(usgs_dataset, **api_search_kwargs)
200
+ # search by id
201
+ if searched_id := kwargs.get("id"):
202
+ dataset_filters = api.dataset_filters(usgs_dataset)
203
+ # ip pattern set as parameter queryable (first element of param conf list)
204
+ id_pattern = self.config.metadata_mapping["id"][0]
205
+ # loop on matching dataset_filters until one returns expected results
206
+ for dataset_filter in dataset_filters["data"]:
207
+ if id_pattern in dataset_filter["searchSql"]:
208
+ logger.debug(
209
+ f"Try using {dataset_filter['searchSql']} dataset filter to search by id on {usgs_dataset}"
210
+ )
211
+ full_api_search_kwargs = {
212
+ "where": {
213
+ "filter_id": dataset_filter["id"],
214
+ "value": searched_id,
215
+ },
216
+ **api_search_kwargs,
217
+ }
218
+ logger.info(
219
+ f"Sending search request for {usgs_dataset} with {full_api_search_kwargs}"
220
+ )
221
+ results = api.scene_search(
222
+ usgs_dataset, **full_api_search_kwargs
223
+ )
224
+ if len(results["data"]["results"]) == 1:
225
+ # search by id using this dataset_filter succeeded
226
+ break
227
+ else:
228
+ logger.info(
229
+ f"Sending search request for {usgs_dataset} with {api_search_kwargs}"
230
+ )
231
+ results = api.scene_search(usgs_dataset, **api_search_kwargs)
180
232
 
181
233
  # update results with storage info from download_options()
182
234
  results_by_entity_id = {
@@ -227,7 +279,7 @@ class UsgsApi(Api):
227
279
  f"Product type {usgs_dataset} may not exist on USGS EE catalog"
228
280
  )
229
281
  api.logout()
230
- raise RequestError(e)
282
+ raise RequestError.from_error(e) from e
231
283
 
232
284
  api.logout()
233
285
 
@@ -257,13 +309,13 @@ class UsgsApi(Api):
257
309
  )
258
310
  progress_callback = ProgressCallback(disable=True)
259
311
 
260
- outputs_extension = cast(
312
+ output_extension = cast(
261
313
  str,
262
314
  self.config.products.get( # type: ignore
263
315
  product.product_type, self.config.products[GENERIC_PRODUCT_TYPE] # type: ignore
264
- ).get("outputs_extension", ".tar.gz"),
316
+ ).get("output_extension", ".tar.gz"),
265
317
  )
266
- kwargs["outputs_extension"] = kwargs.get("outputs_extension", outputs_extension)
318
+ kwargs["output_extension"] = kwargs.get("output_extension", output_extension)
267
319
 
268
320
  fs_path, record_filename = self._prepare_download(
269
321
  product,
@@ -375,8 +427,8 @@ class UsgsApi(Api):
375
427
 
376
428
  # Check downloaded file format
377
429
  if (
378
- kwargs["outputs_extension"] == ".tar.gz" and tarfile.is_tarfile(fs_path)
379
- ) or (kwargs["outputs_extension"] == ".zip" and zipfile.is_zipfile(fs_path)):
430
+ kwargs["output_extension"] == ".tar.gz" and tarfile.is_tarfile(fs_path)
431
+ ) or (kwargs["output_extension"] == ".zip" and zipfile.is_zipfile(fs_path)):
380
432
  product_path = self._finalize(
381
433
  fs_path,
382
434
  progress_callback=progress_callback,
@@ -388,7 +440,7 @@ class UsgsApi(Api):
388
440
  logger.info(
389
441
  "Downloaded product detected as a tar File, but was was expected to be a zip file"
390
442
  )
391
- new_fs_path = fs_path[: fs_path.index(outputs_extension)] + ".tar.gz"
443
+ new_fs_path = fs_path[: fs_path.index(output_extension)] + ".tar.gz"
392
444
  shutil.move(fs_path, new_fs_path)
393
445
  product.location = path_to_uri(new_fs_path)
394
446
  return new_fs_path
@@ -396,7 +448,7 @@ class UsgsApi(Api):
396
448
  logger.info(
397
449
  "Downloaded product detected as a zip File, but was was expected to be a tar file"
398
450
  )
399
- new_fs_path = fs_path[: fs_path.index(outputs_extension)] + ".zip"
451
+ new_fs_path = fs_path[: fs_path.index(output_extension)] + ".zip"
400
452
  shutil.move(fs_path, new_fs_path)
401
453
  product.location = path_to_uri(new_fs_path)
402
454
  return new_fs_path
@@ -404,7 +456,7 @@ class UsgsApi(Api):
404
456
  logger.warning(
405
457
  "Downloaded product is not a tar or a zip File. Please check its file type before using it"
406
458
  )
407
- new_fs_path = fs_path[: fs_path.index(outputs_extension)]
459
+ new_fs_path = fs_path[: fs_path.index(output_extension)]
408
460
  shutil.move(fs_path, new_fs_path)
409
461
  product.location = path_to_uri(new_fs_path)
410
462
  return new_fs_path
@@ -30,13 +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``
38
- - auth using current environment (AWS environment variables and/or ``~/aws/*``),
36
+ * auth anonymously using no-sign-request
37
+ * auth using ``aws_profile``
38
+ * auth using ``aws_access_key_id`` and ``aws_secret_access_key``
39
+ (optionally ``aws_session_token``)
40
+ * auth using current environment (AWS environment variables and/or ``~/aws/*``),
39
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
+
40
50
  """
41
51
 
42
52
  s3_client: S3Client
@@ -45,13 +55,13 @@ class AwsAuth(Authentication):
45
55
  super(AwsAuth, self).__init__(provider, config)
46
56
  self.aws_access_key_id = None
47
57
  self.aws_secret_access_key = None
58
+ self.aws_session_token = None
48
59
  self.profile_name = None
49
60
 
50
61
  def authenticate(self) -> Dict[str, str]:
51
62
  """Authenticate
52
63
 
53
64
  :returns: dict containing AWS/boto3 non-empty credentials
54
- :rtype: dict
55
65
  """
56
66
  credentials = getattr(self.config, "credentials", {}) or {}
57
67
  self.aws_access_key_id = credentials.get(
@@ -60,7 +70,15 @@ class AwsAuth(Authentication):
60
70
  self.aws_secret_access_key = credentials.get(
61
71
  "aws_secret_access_key", self.aws_secret_access_key
62
72
  )
73
+ self.aws_session_token = credentials.get(
74
+ "aws_session_token", self.aws_session_token
75
+ )
63
76
  self.profile_name = credentials.get("aws_profile", self.profile_name)
64
77
 
65
- auth_keys = ["aws_access_key_id", "aws_secret_access_key", "profile_name"]
78
+ auth_keys = [
79
+ "aws_access_key_id",
80
+ "aws_secret_access_key",
81
+ "aws_session_token",
82
+ "profile_name",
83
+ ]
66
84
  return {k: getattr(self, k) for k in auth_keys if getattr(self, k)}
@@ -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)