Skip to content

Commit

Permalink
Fix user/admin routes, check user has access to submission before iss…
Browse files Browse the repository at this point in the history
…uing download token
  • Loading branch information
evanjt committed Jun 11, 2024
1 parent ff4ddb3 commit 1d96919
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 78 deletions.
4 changes: 3 additions & 1 deletion app/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ async def get_user_info(payload: dict = Depends(get_payload)) -> User:
)


async def require_admin(user: User = Depends(get_user_info)):
async def require_admin(
user: User = Depends(get_user_info),
) -> User:
if "admin" not in user.realm_roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
Expand Down
80 changes: 14 additions & 66 deletions app/objects.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
from typing import Any
from fastapi import Depends, APIRouter, Query, Response, Body
from fastapi.responses import StreamingResponse
from app.config import config
from fastapi import Depends, APIRouter, Query
from app.utils import get_async_client, _reverse_proxy
import httpx
from uuid import UUID
from app.models.user import User
from app.auth import require_admin, get_user_info
from fastapi import (
Header,
HTTPException,
Request,
status,
)
from starlette.background import BackgroundTask
from app.auth import get_user_info
from fastapi import Request
from fastapi.responses import PlainTextResponse


Expand Down Expand Up @@ -53,65 +45,25 @@ async def upload_chunk(
request: Request,
patch: str = Query(...),
client: httpx.AsyncClient = Depends(get_async_client),
admin_user: User = Depends(require_admin),
user: User = Depends(get_user_info),
reverse_proxy: Any = Depends(_reverse_proxy),
) -> Any:
"""Creates an object
Forwards the file to the API with multipart form data encoding
"""
try:

URL = f"{config.DEEPREEFMAP_API_URL}/v1/objects?patch={patch}"
req = client.build_request(
"PATCH",
URL,
headers=request.headers.raw,
timeout=None,
content=request.stream(),
)
r = await client.send(req, stream=True)
return StreamingResponse(
r.aiter_raw(),
status_code=r.status_code,
headers=r.headers,
background=BackgroundTask(r.aclose),
)

except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=e.response.text,
)

return reverse_proxy


@router.head("")
async def check_uploaded_chunks(
request: Request,
patch: str = Query(...),
client: httpx.AsyncClient = Depends(get_async_client),
reverse_proxy: Any = Depends(_reverse_proxy),
):
try:
URL = f"{config.DEEPREEFMAP_API_URL}/v1/objects?patch={patch}"
req = client.build_request(
"HEAD",
URL,
headers=request.headers.raw,
content=request.stream(),
)
r = await client.send(req, stream=True)
return StreamingResponse(
r.aiter_raw(),
status_code=r.status_code,
headers=r.headers,
background=BackgroundTask(r.aclose),
)

except httpx.HTTPStatusError as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=e.response.text,
)
"""Check the uploaded chunks"""

return reverse_proxy


@router.get("/{object_id}")
Expand All @@ -136,16 +88,12 @@ async def get_objects(
@router.post("/{object_id}")
async def regenerate_statistics(
object_id: UUID,
client: httpx.AsyncClient = Depends(get_async_client),
admin_user: User = Depends(require_admin),
# client: httpx.AsyncClient = Depends(get_async_client),
reverse_proxy: Any = Depends(_reverse_proxy),
) -> Any:
"""Regenerates the video statistics for the object"""

res = await client.post(
f"{config.DEEPREEFMAP_API_URL}/v1/objects/{object_id}"
)

return res.json()
return reverse_proxy


@router.put("/{object_id}")
Expand Down
39 changes: 29 additions & 10 deletions app/submissions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Any
from fastapi import Depends, APIRouter, Query, Response, Body, HTTPException
from fastapi import Depends, APIRouter, Request, HTTPException
from app.config import config
from app.utils import get_async_client, _reverse_proxy
import httpx
Expand Down Expand Up @@ -35,7 +35,7 @@ async def delete_job(
job_id: str,
client: httpx.AsyncClient = Depends(get_async_client),
*,
user: User = Depends(require_admin),
user: User = Depends(get_user_info),
) -> Any:
"""Delete a kubernetes job by ID"""

Expand Down Expand Up @@ -67,7 +67,6 @@ async def get_submission_output_file(
)

submission_id, filename, exp = decoded.values()
print("EXP", exp, datetime.datetime.fromtimestamp(exp))

if datetime.datetime.fromtimestamp(
exp, tz=datetime.timezone.utc
Expand Down Expand Up @@ -104,6 +103,7 @@ async def get_submission(

@router.get("/{submission_id}/{filename}", response_model=DownloadToken)
async def get_submission_output_file_token(
request: Request,
client: httpx.AsyncClient = Depends(get_async_client),
*,
submission_id: UUID,
Expand All @@ -119,6 +119,30 @@ async def get_submission_output_file_token(
Token expires at a set time defined by config.SERIALIZER_EXPIRY_HOURS
"""

# Get the resource to validate that it exists and the user has access
is_admin = "admin" in user.realm_roles
headers = {
key.decode(): value.decode() for key, value in request.headers.raw
}
headers.update( # Add user ID and roles to the headers
{
"User-ID": user.id,
"User-Is-Admin": str(is_admin),
}
)
req = client.build_request(
"GET",
f"{config.DEEPREEFMAP_API_URL}/v1/submissions/{submission_id}",
headers=headers,
)
r = await client.send(req)

if r.status_code != 200:
raise HTTPException(
status_code=r.status_code,
detail=r.text,
)

payload = {
"submission_id": str(submission_id),
"filename": filename,
Expand All @@ -135,16 +159,11 @@ async def get_submission_output_file_token(
@router.post("/{submission_id}/execute", response_model=Any)
async def execute_submission(
submission_id: UUID,
client: httpx.AsyncClient = Depends(get_async_client),
admin_user: User = Depends(require_admin),
reverse_proxy: Any = Depends(_reverse_proxy),
) -> Any:
"""Execute a submission by id"""

res = await client.post(
f"{config.DEEPREEFMAP_API_URL}/v1/submissions/{submission_id}/execute",
)

return res.json()
return reverse_proxy


@router.get("")
Expand Down
6 changes: 5 additions & 1 deletion app/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ async def _reverse_proxy(
path=path,
query=request.url.query.encode("utf-8"),
)
is_admin = "admin" in user.realm_roles
headers = {
key.decode(): value.decode() for key, value in request.headers.raw
}
headers.update( # Add user ID and roles to the headers
{"User-ID": user.id, "User-Roles": ",".join(user.realm_roles)}
{
"User-ID": user.id,
"User-Is-Admin": str(is_admin),
}
)

req = client.build_request(
Expand Down

0 comments on commit 1d96919

Please sign in to comment.