diff --git a/isic/zip_download/api.py b/isic/zip_download/api.py index a8401d5d..97182e55 100644 --- a/isic/zip_download/api.py +++ b/isic/zip_download/api.py @@ -4,7 +4,7 @@ import json import logging from pathlib import PurePosixPath -from typing import TYPE_CHECKING, Literal +from typing import TYPE_CHECKING, Any, Literal from botocore.signers import CloudFrontSigner from django.conf import settings @@ -63,20 +63,20 @@ def authenticate(self, request: HttpRequest, username: str, password: str) -> Li class ZipDownloadTokenAuth(APIKeyQuery): param_name = "token" - def authenticate(self, request: HttpRequest, key: str | None) -> dict: + def authenticate(self, request: HttpRequest, key: str | None) -> dict | None: if not key: - raise AuthenticationError + return None try: token_dict = TimestampSigner().unsign_object(key, max_age=timedelta(days=1)) except BadSignature: - raise AuthenticationError from None + return None token_dict["token"] = key return token_dict -def zip_api_auth(request: HttpRequest): +def zip_api_auth(request: HttpRequest) -> Any: """ Protects the zip listing endpoint with basic auth. diff --git a/isic/zip_download/tests/test_zip_download.py b/isic/zip_download/tests/test_zip_download.py index 322bf02a..185e6f2d 100644 --- a/isic/zip_download/tests/test_zip_download.py +++ b/isic/zip_download/tests/test_zip_download.py @@ -3,6 +3,8 @@ from urllib.parse import ParseResult, parse_qs, urlparse from django.conf import settings +from django.core.signing import TimestampSigner +from ninja.errors import AuthenticationError import pytest from isic.core.models import Image @@ -48,6 +50,30 @@ def zip_basic_auth(): } +@pytest.mark.django_db +def test_zip_api_auth(mocker, authenticated_client, zip_basic_auth): + from isic.zip_download.api import zip_api_auth + + token = TimestampSigner().sign_object({"token": "foo"}) + + request = mocker.MagicMock( + headers={"Authorization": "Basic " + b64encode(b":badcredentials").decode()} + ) + with pytest.raises(AuthenticationError): + zip_api_auth(request) + + request = mocker.MagicMock( + headers={"Authorization": zip_basic_auth["HTTP_AUTHORIZATION"]}, + GET={"token": token}, + ) + assert zip_api_auth(request) + + request = mocker.MagicMock( + headers={"Authorization": zip_basic_auth["HTTP_AUTHORIZATION"]}, GET={"token": "badtoken"} + ) + assert not zip_api_auth(request) + + @pytest.mark.django_db(transaction=True) @pytest.mark.usefixtures("_random_images_with_licenses") def test_zip_download_licenses(authenticated_client, zip_basic_auth):