Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions apps/jobs/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import os
import uuid
from urllib.parse import urlparse

import botocore
from accounts.permissions import HasVerifiedEmail
Expand Down Expand Up @@ -95,6 +96,39 @@
logger = logging.getLogger(__name__)


def _get_allowed_submission_file_types(challenge_phase):
allowed_file_types = challenge_phase.allowed_submission_file_types or ""
return [
(
file_type.strip().lower()
if file_type.strip().startswith(".")
else ".{}".format(file_type.strip().lower())
)
for file_type in allowed_file_types.split(",")
if file_type.strip()
]


def _is_submission_file_type_allowed(file_name, challenge_phase):
allowed_file_types = _get_allowed_submission_file_types(challenge_phase)
if not allowed_file_types:
return True

file_path = urlparse(file_name).path.lower()
return any(
file_path.endswith(file_type) for file_type in allowed_file_types
)


def _invalid_submission_file_type_response(challenge_phase):
response_data = {
"error": "Invalid submission file type. Allowed file types are: {}.".format(
challenge_phase.allowed_submission_file_types
)
}
return Response(response_data, status=status.HTTP_400_BAD_REQUEST)


@extend_schema(
methods=["POST"],
parameters=[
Expand Down Expand Up @@ -334,6 +368,10 @@ def challenge_submission(request, challenge_id, challenge_phase_id):
return Response(
response_data, status=status.HTTP_400_BAD_REQUEST
)
if not _is_submission_file_type_allowed(
request.data["file_url"], challenge_phase
):
return _invalid_submission_file_type_response(challenge_phase)
download_file_and_publish_submission_message.delay(
request.data,
request.user.id,
Expand All @@ -345,6 +383,12 @@ def challenge_submission(request, challenge_id, challenge_phase_id):
}
return Response(response_data, status=status.HTTP_200_OK)

input_file = request.FILES.get("input_file")
if input_file and not _is_submission_file_type_allowed(
input_file.name, challenge_phase
):
return _invalid_submission_file_type_response(challenge_phase)

if request.data.get("submission_meta_attributes"):
submission_meta_attributes = json.load(
request.data.get("submission_meta_attributes")
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/jobs/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,73 @@ def test_challenge_submission_for_successful_submission(self):
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)

def test_challenge_submission_rejects_disallowed_upload_file_type(self):
self.url = reverse_lazy(
"jobs:challenge_submission",
kwargs={
"challenge_id": self.challenge.pk,
"challenge_phase_id": self.challenge_phase.pk,
},
)

self.challenge.participant_teams.add(self.participant_team)
self.challenge.save()
invalid_input_file = SimpleUploadedFile(
"submission.png", b"file_content", content_type="image/png"
)

response = self.client.post(
self.url,
{"status": "submitting", "input_file": invalid_input_file},
format="multipart",
)

expected = {
"error": "Invalid submission file type. Allowed file types are: {}.".format(
self.challenge_phase.allowed_submission_file_types
)
}
self.assertEqual(response.data, expected)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

@mock.patch(
"jobs.views.download_file_and_publish_submission_message.delay"
)
@mock.patch("jobs.views.is_url_valid", return_value=True)
def test_challenge_submission_rejects_disallowed_file_url_type(
self, mock_is_url_valid, mock_delay
):
self.url = reverse_lazy(
"jobs:challenge_submission",
kwargs={
"challenge_id": self.challenge.pk,
"challenge_phase_id": self.challenge_phase.pk,
},
)

self.challenge.participant_teams.add(self.participant_team)
self.challenge.save()

response = self.client.post(
self.url,
{
"status": "submitting",
"file_url": "http://example.com/submission.png",
},
)

expected = {
"error": "Invalid submission file type. Allowed file types are: {}.".format(
self.challenge_phase.allowed_submission_file_types
)
}
self.assertEqual(response.data, expected)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
mock_is_url_valid.assert_called_once_with(
"http://example.com/submission.png"
)
mock_delay.assert_not_called()

@mock.patch("jobs.views.ensure_workers_for_submission")
def test_challenge_submission_calls_ensure_workers_for_participant(
self, mock_ensure_workers
Expand Down