Skip to content

Commit

Permalink
Refactor and enable downloads for private systems
Browse files Browse the repository at this point in the history
  • Loading branch information
jarosenb committed Jan 12, 2022
1 parent 3f167b3 commit 65d0d05
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 89 deletions.
51 changes: 25 additions & 26 deletions server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def raise_for_size(size: int, max_size: int = 2e9) -> None:

class Archive(TypedDict):
fs: str # Represents the absolute path to a file on the host machine.
n: str # Represents the path to a file relative to the zip archive root.
n: str # Represents the path to a file within the zip archive.


def walk_archive_paths(base_path: str, file_paths: List[str]) -> List[Archive]:
Expand Down Expand Up @@ -82,6 +82,24 @@ def walk_archive_paths(base_path: str, file_paths: List[str]) -> List[Archive]:
return zip_paths


def check_system_access(system: str, paths: List[str], token: str) -> None:
"""
Confirm a user's READ access to files in a system by using their Tapis access token
to perform a listing at the files' common path.
"""
try:
common_path = os.path.commonpath(map(lambda p: p.strip("/"), paths))
listing_url = (
f"{TAPIS_BASE_URL}"
"/files/v2/listings/system/"
f"{system}/{common_path}/?limit=1"
)
resp = requests.get(listing_url, headers={"Authorization": f"Bearer {token}"})
resp.raise_for_status()
except HTTPError:
raise HTTPException(status_code=resp.status_code, detail=resp.reason)


class CheckResponse(BaseModel):
key: str

Expand All @@ -97,33 +115,14 @@ def check_downloadable(
auth: http.HTTPAuthorizationCredentials = Depends(HTTPBearer(auto_error=False)),
):
PUBLIC_SYSTEMS = ["designsafe.storage.community", "designsafe.storage.published"]
if request.system not in PUBLIC_SYSTEMS:

# Limit API to public systems for initial release.
if not auth and request.system not in PUBLIC_SYSTEMS:
raise HTTPException(
status_code=403, detail="Private systems are currently disabled."
status_code=401,
detail="This resource cannot be accessed without Tapis credentials.",
)
"""
if not auth:
raise HTTPException(
status_code=401,
detail="Attempt to access private resource without auth credentials.",
)
try:
listing_url = (
f"{TAPIS_BASE_URL}"
"/files/v2/listings/system/"
f"{request.system}/{request.path.strip('/')}"
)
resp = requests.get(
listing_url, headers={"Authorization": f"Bearer {auth.credentials}"}
)
resp.raise_for_status()
return {"message": "success"}
except HTTPError:
print(resp.content)
raise HTTPException(status_code=403, detail=resp.json())
"""
elif request.system not in PUBLIC_SYSTEMS:
check_system_access(request.system, request.paths, auth.credentials)

system_root = get_system_root(request.system)
paths = walk_archive_paths(system_root, request.paths)

Expand Down
122 changes: 122 additions & 0 deletions server/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import pytest
import os
from fastapi.exceptions import HTTPException
from unittest.mock import MagicMock, create_autospec
from server.main import get_system_root, walk_archive_paths, check_system_access


def test_get_system_root():
assert (
get_system_root("designsafe.storage.default")
== "/corral-repl/tacc/NHERI/shared"
)
assert (
get_system_root("designsafe.storage.community")
== "/corral-repl/tacc/NHERI/community"
)
assert (
get_system_root("designsafe.storage.published")
== "/corral-repl/tacc/NHERI/published"
)
assert (
get_system_root("project-7448086614930166251-242ac113-0001-012")
== "/corral-repl/tacc/NHERI/projects/7448086614930166251-242ac113-0001-012"
)
with pytest.raises(HTTPException):
get_system_root("not-a-real-system")


def test_walk_archive(tmp_path):
"""
Generate and walk the following directory structure at tmp_path:
.
├── f1.txt
└── sub_path/
├── f2.txt
└── sub_path2/
└── f3.txt
"""
base = tmp_path

f1 = base / "f1.txt"
f1.write_text("CONTENT 1")

sub = base / "sub_path"
os.mkdir(sub)
f2 = sub / "f2.txt"
f2.write_text("CONTENT 2")

sub2 = sub / "sub_path2"
os.mkdir(sub2)
f3 = sub2 / "f3.txt"
f3.write_text("CONTENT 3")

requested_files = ["f1.txt", "sub_path"]
walk_result = walk_archive_paths(base, requested_files)

assert walk_result == [
{"fs": str(base / "f1.txt"), "n": "f1.txt"},
{"fs": str(base / "sub_path" / "f2.txt"), "n": "sub_path/f2.txt"},
{
"fs": str(base / "sub_path" / "sub_path2" / "f3.txt"),
"n": "sub_path/sub_path2/f3.txt",
},
]


@pytest.fixture
def mock_get_success(monkeypatch, request):
import requests

mock_fn = create_autospec(requests.get)
mock_response = requests.Response()
mock_response.status_code = 200

mock_fn.return_value = mock_response
monkeypatch.setattr(requests, "get", mock_fn)
yield mock_fn


@pytest.fixture
def mock_get_error(monkeypatch):
import requests

mock_fn = create_autospec(requests.get)
mock_response = requests.Response()
mock_response.status_code = 404

mock_fn.return_value = mock_response
monkeypatch.setattr(requests, "get", mock_fn)
yield mock_fn


def test_check_system_access_success(mock_get_success, monkeypatch):
from server import main

monkeypatch.setattr(main, "TAPIS_BASE_URL", "tapis.test")
try:
check_system_access(
"test.system", ["/tmp/file1.txt", "/tmp/tmp2/file2.txt"], "ABC123"
)
except Exception:
assert 0

mock_get_success.assert_called_with(
"tapis.test/files/v2/listings/system/test.system/tmp/?limit=1",
headers={"Authorization": f"Bearer {'ABC123'}"},
)


def test_check_system_access_failure(mock_get_error, monkeypatch):
from server import main

monkeypatch.setattr(main, "TAPIS_BASE_URL", "tapis.test")
with pytest.raises(HTTPException):
check_system_access(
"test.system", ["/tmp/file1.txt", "/tmp/tmp2/file2.txt"], "ABC123"
)

mock_get_error.assert_called_with(
"tapis.test/files/v2/listings/system/test.system/tmp/?limit=1",
headers={"Authorization": f"Bearer {'ABC123'}"},
)
63 changes: 0 additions & 63 deletions server/tests/util_test.py

This file was deleted.

0 comments on commit 65d0d05

Please sign in to comment.