-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #957 from uc-cdis/feat/ga4gh_drs
feat(ga4gh): move DRS access API to fence
- Loading branch information
Showing
8 changed files
with
317 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import flask | ||
from fence.errors import UserError | ||
|
||
from fence.blueprints.data.indexd import ( | ||
get_signed_url_for_file, | ||
) | ||
|
||
blueprint = flask.Blueprint("ga4gh", __name__) | ||
|
||
|
||
@blueprint.route( | ||
"/drs/v1/objects/<path:object_id>/access", | ||
defaults={"access_id": None}, | ||
methods=["GET"], | ||
) | ||
@blueprint.route( | ||
"/drs/v1/objects/<path:object_id>/access/<path:access_id>", | ||
methods=["GET", "POST"], | ||
) | ||
def get_ga4gh_signed_url(object_id, access_id): | ||
if not access_id: | ||
raise UserError("Access ID/Protocol is required.") | ||
|
||
result = get_signed_url_for_file( | ||
"download", object_id, requested_protocol=access_id | ||
) | ||
return flask.jsonify(result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[tool.poetry] | ||
name = "fence" | ||
version = "5.4.1" | ||
version = "5.5.0" | ||
description = "Gen3 AuthN/AuthZ OIDC Service" | ||
authors = ["CTDS UChicago <[email protected]>"] | ||
license = "Apache-2.0" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
import json | ||
import jwt | ||
import pytest | ||
import requests | ||
import responses | ||
from tests import utils | ||
|
||
|
||
def get_doc(has_version=True, urls=list(), drs_list=0): | ||
doc = { | ||
"form": "object", | ||
"size": 123, | ||
"urls": ["s3://endpointurl/bucket/key"], | ||
"hashes": {"md5": "1234"}, | ||
} | ||
if has_version: | ||
doc["version"] = "1" | ||
if urls: | ||
doc["urls"] = urls | ||
|
||
return doc | ||
|
||
|
||
@responses.activate | ||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_unauthorized( | ||
client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
user = {"Authorization": "Bearer INVALID"} | ||
|
||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" + test_guid + f"/access/{access_id}", | ||
headers=user, | ||
) | ||
assert res.status_code == 401 | ||
|
||
|
||
@responses.activate | ||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_with_access_id( | ||
client, | ||
user_client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
user = { | ||
"Authorization": "Bearer " | ||
+ jwt.encode( | ||
utils.authorized_download_context_claims( | ||
user_client.username, user_client.user_id | ||
), | ||
key=rsa_private_key, | ||
headers={"kid": kid}, | ||
algorithm="RS256", | ||
).decode("utf-8") | ||
} | ||
|
||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" + test_guid + "/access/" + access_id, | ||
headers=user, | ||
) | ||
assert res.status_code == 200 | ||
|
||
|
||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_no_access_id( | ||
client, | ||
user_client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
user = { | ||
"Authorization": "Bearer " | ||
+ jwt.encode( | ||
utils.authorized_download_context_claims( | ||
user_client.username, user_client.user_id | ||
), | ||
key=rsa_private_key, | ||
headers={"kid": kid}, | ||
algorithm="RS256", | ||
).decode("utf-8") | ||
} | ||
|
||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" + test_guid + "/access/", | ||
headers=user, | ||
) | ||
assert res.status_code == 400 | ||
|
||
|
||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_no_bearer_token( | ||
client, | ||
indexd_client, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
|
||
res = client.get("/ga4gh/drs/v1/objects/" + test_guid + f"/access/{access_id}") | ||
assert res.status_code == 401 | ||
|
||
|
||
@responses.activate | ||
def test_get_presigned_url_wrong_access_id( | ||
client, | ||
user_client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
test_guid = "1" | ||
user = { | ||
"Authorization": "Bearer " | ||
+ jwt.encode( | ||
utils.authorized_download_context_claims( | ||
user_client.username, user_client.user_id | ||
), | ||
key=rsa_private_key, | ||
headers={"kid": kid}, | ||
algorithm="RS256", | ||
).decode("utf-8") | ||
} | ||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" + test_guid + "/access/s2", | ||
headers=user, | ||
) | ||
assert res.status_code == 404 | ||
|
||
|
||
@responses.activate | ||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_with_encoded_slash( | ||
client, | ||
user_client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
user = { | ||
"Authorization": "Bearer " | ||
+ jwt.encode( | ||
utils.authorized_download_context_claims( | ||
user_client.username, user_client.user_id | ||
), | ||
key=rsa_private_key, | ||
headers={"kid": kid}, | ||
algorithm="RS256", | ||
).decode("utf-8") | ||
} | ||
data = get_doc() | ||
data["did"] = "dg.TEST/ed8f4658-6acd-4f96-9dd8-3709890c959e" | ||
did = "dg.TEST%2Fed8f4658-6acd-4f96-9dd8-3709890c959e" | ||
|
||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" + did + "/access/" + access_id, | ||
headers=user, | ||
) | ||
assert res.status_code == 200 | ||
|
||
|
||
@responses.activate | ||
@pytest.mark.parametrize("indexd_client", ["s3", "gs"], indirect=True) | ||
def test_get_presigned_url_with_query_params( | ||
client, | ||
user_client, | ||
indexd_client, | ||
kid, | ||
rsa_private_key, | ||
google_proxy_group, | ||
primary_google_service_account, | ||
cloud_manager, | ||
google_signed_url, | ||
): | ||
access_id = indexd_client["indexed_file_location"] | ||
test_guid = "1" | ||
user = { | ||
"Authorization": "Bearer " | ||
+ jwt.encode( | ||
utils.authorized_download_context_claims( | ||
user_client.username, user_client.user_id | ||
), | ||
key=rsa_private_key, | ||
headers={"kid": kid}, | ||
algorithm="RS256", | ||
).decode("utf-8") | ||
} | ||
data = get_doc() | ||
data["did"] = "dg.TEST/ed8f4658-6acd-4f96-9dd8-3709890c959e" | ||
did = "dg.TEST%2Fed8f4658-6acd-4f96-9dd8-3709890c959e" | ||
|
||
res = client.get( | ||
"/ga4gh/drs/v1/objects/" | ||
+ did | ||
+ "/access/" | ||
+ access_id | ||
+ "?userProject=someproject&arbitrary_parameter=val", | ||
headers=user, | ||
) | ||
assert res.status_code == 200 |