Skip to content

Commit

Permalink
Check access when creating work order token
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito committed Jul 25, 2024
1 parent 8d91843 commit f387cc7
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 17 deletions.
19 changes: 18 additions & 1 deletion src/wps/core/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ async def create(
if work_type == WorkType.DOWNLOAD:
if not await self._access.check_download_access(user_id, dataset_id):
access_error = self.WorkPackageAccessError(
"Missing dataset access permission"
"Dataset access permission has not been granted"
)
log.error(access_error, extra=extra)
raise access_error
Expand Down Expand Up @@ -257,6 +257,7 @@ async def work_order_token(
- if check_valid is set and the work package has expired
- if a work_package_access_token is specified and it does not match
the token hash that is stored in the work package
- if the access permission has been revoked
"""
extra = { # only used for logging
"work_package_id": work_package_id,
Expand All @@ -277,6 +278,22 @@ async def work_order_token(
log.error(access_error, extra=extra)
raise access_error

if work_package.type == WorkType.DOWNLOAD:
# since the work package access token is long-lived,
# we double-check that the user still has access
if not await self._access.check_download_access(
work_package.user_id, work_package.dataset_id
):
access_error = self.WorkPackageAccessError(
"Dataset access permission has been revoked"
)
log.error(access_error, extra=extra)
raise access_error
else:
access_error = self.WorkPackageAccessError("Unsupported work type")
log.error(access_error, extra=extra)
raise access_error

user_public_crypt4gh_key = work_package.user_public_crypt4gh_key
wot = WorkOrderToken(
type=work_package.type,
Expand Down
1 change: 0 additions & 1 deletion tests/fixtures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"fixture_config",
"fixture_repository",
"fixture_client",
"fixture_consumer",
"headers_for_token",
"non_mocked_hosts",
"Consumer",
Expand Down
48 changes: 33 additions & 15 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def test_create_work_order_token(
mongodb_populated: MongoDbFixture,
):
"""Test that work order tokens can be properly created."""
# mock the access check for the test dataset
# mock the access check for the test dataset to grant access

httpx_mock.add_response(
method="GET",
Expand Down Expand Up @@ -186,29 +186,29 @@ async def test_create_work_order_token(
)
assert response.status_code == status.HTTP_201_CREATED

token = response.json()
assert isinstance(token, str)
wot = response.json()
assert isinstance(wot, str)

# decrypt the work order token

assert token and isinstance(token, str) and token.isascii()
token = decrypt(token)
assert wot and isinstance(wot, str) and wot.isascii()
wot = decrypt(wot)

# validate the work order token

assert isinstance(token, str)
assert len(token) > 80
assert token.count(".") == 2
token_chars = token.replace(".", "").replace("-", "").replace("_", "")
assert token_chars.isalnum()
assert token_chars.isascii()
token_dict = decode_and_validate_token(token, SIGNING_KEY_PAIR.public())
assert isinstance(wot, str)
assert len(wot) > 80
assert wot.count(".") == 2
wot_chars = wot.replace(".", "").replace("-", "").replace("_", "")
assert wot_chars.isalnum()
assert wot_chars.isascii()
wot_dict = decode_and_validate_token(wot, SIGNING_KEY_PAIR.public())

# check the content of the work order token

assert isinstance(token_dict, dict)
assert token_dict.pop("exp") - token_dict.pop("iat") == 30
assert token_dict == {
assert isinstance(wot_dict, dict)
assert wot_dict.pop("exp") - wot_dict.pop("iat") == 30
assert wot_dict == {
"type": "download",
"file_id": "file-id-3",
"user_id": "john-doe@ghga.de",
Expand All @@ -217,6 +217,24 @@ async def test_create_work_order_token(
"email": "john@home.org",
}

# mock the access check for the test dataset to revoke access

httpx_mock.reset(assert_all_responses_were_requested=True)
httpx_mock.add_response(
method="GET",
url="http://access/users/john-doe@ghga.de/datasets/some-dataset-id",
text="false",
)

# try to fetch a work order token again

response = await client.post(
f"/work-packages/{work_package_id}/files/file-id-3/work-order-tokens",
headers=headers_for_token(token),
)
assert response.status_code == status.HTTP_403_FORBIDDEN
assert "Access has been revoked" in response.json()["detail"]


async def test_get_datasets_unauthenticated(client: AsyncTestClient):
"""Test that the list of accessible datasets cannot be fetched unauthenticated."""
Expand Down
17 changes: 17 additions & 0 deletions tests/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,23 @@ async def test_work_package_and_token_creation(
"email": package.email,
}

# revoke access and check that work order token cannot be created any more
async def check_download_access_patched(user_id: str, dataset_id: str) -> bool:
return (user_id, dataset_id) != (package.user_id, package.dataset_id)

access = repository._access
_check_download_access_original = access.check_download_access
try:
access.check_download_access = check_download_access_patched # type: ignore
with pytest.raises(repository.WorkPackageAccessError):
await repository.work_order_token(
work_package_id=work_package_id,
file_id="file-id-1",
work_package_access_token=wpat,
)
finally:
access.check_download_access = _check_download_access_original # type: ignore


async def test_checking_accessible_datasets(
repository: WorkPackageRepository,
Expand Down

0 comments on commit f387cc7

Please sign in to comment.