Skip to content

Commit 4f41fb7

Browse files
authored
Merge pull request #210 from ecmwf-projects/COPDS-1932-dataset-licences-url
Show dataset licences URL in PermiessionDenied message
2 parents 2d21f1e + e187203 commit 4f41fb7

File tree

4 files changed

+66
-41
lines changed

4 files changed

+66
-41
lines changed

cads_processing_api_service/auth.py

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,25 @@ def get_accepted_licences(auth_header: tuple[str, str]) -> set[tuple[str, int]]:
183183
return accepted_licences
184184

185185

186-
def check_licences(
187-
required_licences: set[tuple[str, int]], accepted_licences: set[tuple[str, int]]
186+
def verify_licences(
187+
accepted_licences: set[tuple[str, int]] | list[tuple[str, int]],
188+
required_licences: set[tuple[str, int]] | list[tuple[str, int]],
189+
api_request_url: str,
190+
process_id: str,
188191
) -> set[tuple[str, int]]:
189-
"""Check if accepted licences satisfy required ones.
192+
"""
193+
Verify if all the licences required for the process submission have been accepted.
190194
191195
Parameters
192196
----------
193-
required_licences : set[tuple[str, int]]
194-
Required licences.
195-
accepted_licences : set[tuple[str, int]]
196-
Accepted licences.
197+
accepted_licences : set[tuple[str, int]] | list[tuple[str, int]],
198+
Licences accepted by a user stored in the Extended Profiles database.
199+
required_licences : set[tuple[str, int]] | list[tuple[str, int]],
200+
Licences bound to the required process/dataset.
201+
api_request_url : str
202+
API request URL, required to generate the URL to the dataset licences page.
203+
process_id : str
204+
Process identifier, required to generate the URL to the dataset licences page.
197205
198206
Returns
199207
-------
@@ -205,39 +213,30 @@ def check_licences(
205213
exceptions.PermissionDenied
206214
Raised if not all required licences have been accepted.
207215
"""
216+
if not isinstance(accepted_licences, set):
217+
accepted_licences = set(accepted_licences)
218+
if not isinstance(required_licences, set):
219+
required_licences = set(required_licences)
208220
missing_licences = required_licences - accepted_licences
209221
if not len(missing_licences) == 0:
210-
missing_licences_detail = [
211-
{"id": licence[0], "revision": licence[1]} for licence in missing_licences
212-
]
222+
missing_licences_message_template = (
223+
config.ensure_settings().missing_licences_message
224+
)
225+
dataset_licences_url_template = config.ensure_settings().dataset_licences_url
226+
parsed_api_request_url = urllib.parse.urlparse(api_request_url)
227+
base_url = f"{parsed_api_request_url.scheme}://{parsed_api_request_url.netloc}"
228+
dataset_licences_url = dataset_licences_url_template.format(
229+
base_url=base_url, process_id=process_id
230+
)
231+
missing_licences_message = missing_licences_message_template.format(
232+
dataset_licences_url=dataset_licences_url
233+
)
213234
raise exceptions.PermissionDenied(
214-
title="required licences not accepted",
215-
detail=(
216-
"required licences not accepted; "
217-
"please accept the following licences to proceed: "
218-
f"{missing_licences_detail}"
219-
),
235+
title="required licences not accepted", detail=missing_licences_message
220236
)
221237
return missing_licences
222238

223239

224-
def validate_licences(
225-
accepted_licences: set[tuple[str, str]],
226-
licences: list[tuple[str, int]],
227-
) -> None:
228-
"""Validate process execution request's payload in terms of required licences.
229-
230-
Parameters
231-
----------
232-
stored_accepted_licences : set[tuple[str, str]]
233-
Licences accepted by a user stored in the Extended Profiles database.
234-
licences : list[tuple[str, int]]
235-
Licences bound to the required process/dataset.
236-
"""
237-
required_licences = set(licences)
238-
check_licences(required_licences, accepted_licences) # type: ignore
239-
240-
241240
def verify_if_disabled(disabled_reason: str | None, user_role: str | None) -> None:
242241
"""Verify if a dataset's disabling reason grant access to the dataset for a specific user role.
243242

cads_processing_api_service/clients.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ def post_process_execution(
201201
202202
Parameters
203203
----------
204+
api_request: fastapi.Request
205+
API Request object.
204206
process_id : str
205207
Process identifier.
206208
execution_content : models.Execute
@@ -243,15 +245,21 @@ def post_process_execution(
243245
) as exc:
244246
raise exceptions.InvalidRequest(detail=str(exc)) from exc
245247
costs = auth.verify_cost(request_inputs, adaptor_properties, request_origin)
246-
licences = adaptor.get_licences(request_inputs)
248+
required_licences = adaptor.get_licences(request_inputs)
247249
if user_uid != "anonymous":
248250
accepted_licences = auth.get_accepted_licences(auth_header)
249-
auth.validate_licences(accepted_licences, licences)
251+
api_request_url = str(api_request.url)
252+
_ = auth.verify_licences(
253+
accepted_licences, required_licences, api_request_url, process_id
254+
)
250255
job_message = None
251256
else:
252257
job_message = config.ensure_settings().anonymous_licences_message.format(
253258
licences="; ".join(
254-
[f"{licence[0]} (rev: {licence[1]})" for licence in licences]
259+
[
260+
f"{licence[0]} (rev: {licence[1]})"
261+
for licence in required_licences
262+
]
255263
)
256264
)
257265
job_id = str(uuid.uuid4())

cads_processing_api_service/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@
4242
"If you are using cdsapi, please upgrade to the latest version."
4343
)
4444

45+
46+
MISSING_LICENCES_MESSAGE = (
47+
"Not all the required licences have been accepted; "
48+
"please visit {dataset_licences_url} "
49+
"to accept the required licence(s)."
50+
)
51+
52+
4553
general_settings = None
4654

4755

@@ -66,6 +74,10 @@ class Settings(pydantic_settings.BaseSettings):
6674
missing_dataset_title: str = "Dataset not available"
6775
anonymous_licences_message: str = ANONYMOUS_LICENCES_MESSAGE
6876
deprecation_warning_message: str = DEPRECATION_WARNING_MESSAGE
77+
missing_licences_message: str = MISSING_LICENCES_MESSAGE
78+
dataset_licences_url: str = (
79+
"{base_url}/datasets/{process_id}?tab=download#manage-licences"
80+
)
6981

7082
download_nodes_config: str = "/etc/retrieve-api/download-nodes.config"
7183

tests/test_30_auth.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,22 @@
2222
from cads_processing_api_service import auth, exceptions, models
2323

2424

25-
def test_check_licences() -> None:
26-
required_licences = {("licence_1", 1), ("licence_2", 2)}
25+
def test_verify_licences() -> None:
2726
accepted_licences = {("licence_1", 1), ("licence_2", 2), ("licence_3", 3)}
28-
missing_licences = auth.check_licences(required_licences, accepted_licences)
27+
required_licences = {("licence_1", 1), ("licence_2", 2)}
28+
api_request_url = "http://base_url/api/v1/processes/process_id/execution"
29+
process_id = "process_id"
30+
missing_licences = auth.verify_licences(
31+
accepted_licences, required_licences, api_request_url, process_id
32+
)
2933
assert len(missing_licences) == 0
3034

31-
required_licences = {("licence_1", 1), ("licence_2", 2)}
3235
accepted_licences = {("licence_1", 1), ("licence_2", 1)}
36+
required_licences = {("licence_1", 1), ("licence_2", 2)}
3337
with pytest.raises(exceptions.PermissionDenied):
34-
missing_licences = auth.check_licences(required_licences, accepted_licences)
38+
missing_licences = auth.verify_licences(
39+
accepted_licences, required_licences, api_request_url, process_id
40+
)
3541

3642

3743
def test_verify_permission() -> None:

0 commit comments

Comments
 (0)