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.
- eodag/api/core.py +292 -198
- eodag/api/product/_assets.py +6 -6
- eodag/api/product/_product.py +18 -18
- eodag/api/product/metadata_mapping.py +51 -14
- eodag/api/search_result.py +29 -3
- eodag/cli.py +57 -20
- eodag/config.py +413 -117
- eodag/plugins/apis/base.py +10 -4
- eodag/plugins/apis/ecmwf.py +49 -16
- eodag/plugins/apis/usgs.py +30 -7
- eodag/plugins/authentication/aws_auth.py +14 -5
- eodag/plugins/authentication/base.py +10 -1
- eodag/plugins/authentication/generic.py +14 -3
- eodag/plugins/authentication/header.py +12 -4
- eodag/plugins/authentication/keycloak.py +41 -22
- eodag/plugins/authentication/oauth.py +11 -1
- eodag/plugins/authentication/openid_connect.py +178 -163
- eodag/plugins/authentication/qsauth.py +12 -4
- eodag/plugins/authentication/sas_auth.py +19 -2
- eodag/plugins/authentication/token.py +93 -15
- eodag/plugins/authentication/token_exchange.py +19 -19
- eodag/plugins/crunch/base.py +4 -1
- eodag/plugins/crunch/filter_date.py +5 -2
- eodag/plugins/crunch/filter_latest_intersect.py +5 -4
- eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
- eodag/plugins/crunch/filter_overlap.py +5 -7
- eodag/plugins/crunch/filter_property.py +6 -6
- eodag/plugins/download/aws.py +50 -34
- eodag/plugins/download/base.py +41 -50
- eodag/plugins/download/creodias_s3.py +40 -2
- eodag/plugins/download/http.py +221 -195
- eodag/plugins/download/s3rest.py +25 -25
- eodag/plugins/manager.py +168 -23
- eodag/plugins/search/base.py +106 -39
- eodag/plugins/search/build_search_result.py +1065 -324
- eodag/plugins/search/cop_marine.py +112 -29
- eodag/plugins/search/creodias_s3.py +45 -24
- eodag/plugins/search/csw.py +41 -1
- eodag/plugins/search/data_request_search.py +109 -9
- eodag/plugins/search/qssearch.py +549 -257
- eodag/plugins/search/static_stac_search.py +20 -21
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +577 -87
- eodag/resources/providers.yml +1619 -2776
- eodag/resources/stac.yml +3 -163
- eodag/resources/user_conf_template.yml +112 -97
- eodag/rest/config.py +1 -2
- eodag/rest/constants.py +0 -1
- eodag/rest/core.py +138 -98
- eodag/rest/errors.py +181 -0
- eodag/rest/server.py +55 -329
- eodag/rest/stac.py +93 -544
- eodag/rest/types/eodag_search.py +19 -8
- eodag/rest/types/queryables.py +6 -8
- eodag/rest/types/stac_search.py +11 -2
- eodag/rest/utils/__init__.py +3 -0
- eodag/types/__init__.py +71 -18
- eodag/types/download_args.py +3 -3
- eodag/types/queryables.py +180 -73
- eodag/types/search_args.py +3 -3
- eodag/types/whoosh.py +126 -0
- eodag/utils/__init__.py +147 -66
- eodag/utils/exceptions.py +47 -26
- eodag/utils/logging.py +37 -77
- eodag/utils/repr.py +65 -6
- eodag/utils/requests.py +11 -13
- eodag/utils/stac_reader.py +1 -1
- {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/METADATA +80 -81
- eodag-3.1.0b1.dist-info/RECORD +108 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/WHEEL +1 -1
- {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/entry_points.txt +4 -2
- eodag/resources/constraints/climate-dt.json +0 -13
- eodag/resources/constraints/extremes-dt.json +0 -8
- eodag/utils/constraints.py +0 -244
- eodag-3.0.0b3.dist-info/RECORD +0 -110
- {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/LICENSE +0 -0
- {eodag-3.0.0b3.dist-info → eodag-3.1.0b1.dist-info}/top_level.txt +0 -0
eodag/plugins/apis/base.py
CHANGED
|
@@ -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
|
|
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/
|
|
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
|
"""
|
eodag/plugins/apis/ecmwf.py
CHANGED
|
@@ -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
|
|
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,
|
|
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.
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
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, "
|
|
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:
|
|
161
|
-
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:
|
|
251
|
-
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)
|
eodag/plugins/apis/usgs.py
CHANGED
|
@@ -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:
|
|
278
|
-
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.
|
|
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:
|
|
448
|
-
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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"
|
|
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
|
-
|
|
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
|
|
51
|
-
authentication process as-is, by giving their names (e.g.
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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
|
-
|
|
105
|
-
self.token_info["access_token"] = access_token
|
|
126
|
+
self._get_access_token()
|
|
106
127
|
return CodeAuthorizedAuth(
|
|
107
|
-
self.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|