Skip to content

Commit c7498dc

Browse files
authored
fix(submission): handle missing xml file with digest authentication DEV-1038 (#6428)
### 📣 Summary Prevent server errors when clients using Digest authentication send empty submission requests to the openrosa `/<username>/submission` endpoint. ### 📖 Description Previously, when a client (such as curl using --digest) attempted to submit to `https://kobocat/<username>/submission` for a form that does not allow anonymous submissions, the first unauthenticated request in the Digest handshake was accepted by the view. Because this initial request contained no body and no authentication header, the view attempted to read a `None` file instance, leading to: ``` AttributeError: 'NoneType' object has no attribute 'read' ``` This caused a 500 error before the client’s second (authenticated) request could be processed. This PR adds a validation to check whether `xml_file` is missing or empty. When this happens, the server now fails fast with a proper `OpenRosaResponseBadRequest (400)` and a clear, informative error message.
1 parent 48e95bf commit c7498dc

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

kobo/apps/openrosa/apps/api/tests/viewsets/test_xform_submission_api.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,40 @@ def test_submission_data_collector(self):
921921
f'http://testserver/collector/{dc.token}/submission',
922922
)
923923

924+
def test_digest_auth_allows_submission_on_username_endpoint(self):
925+
"""
926+
Test that Digest authentication works correctly on the
927+
`/<username>/submission` endpoint when the xform requires auth
928+
"""
929+
username = self.user.username
930+
931+
# Ensure that POST to `/<username>/submission` fails without auth
932+
request = self.factory.post(f'/{username}/submission')
933+
response = self.view(request, username=username)
934+
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
935+
936+
# Ensure that POST to `/<username>/submission` with Digest auth
937+
s = self.surveys[0]
938+
submission_path = os.path.join(
939+
self.main_directory, 'fixtures',
940+
'transportation', 'instances', s, s + '.xml'
941+
)
942+
with open(submission_path) as sf:
943+
request = self.factory.post(f'/{username}/submission', data={})
944+
response = self.view(request, username=username)
945+
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
946+
947+
data = {'xml_submission_file': sf}
948+
949+
request = self.factory.post(f'/{username}/submission', data)
950+
auth = DigestAuth('bob', 'bobbob')
951+
request.META.update(auth(request.META, response))
952+
953+
response = self.view(request, username=username)
954+
self.assertContains(
955+
response, 'Successful submission', status_code=status.HTTP_201_CREATED
956+
)
957+
924958

925959
class ConcurrentSubmissionTestCase(RequestMixin, LiveServerTestCase):
926960
"""

kobo/apps/openrosa/apps/api/viewsets/xform_submission_api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from django.utils.translation import gettext as t
66
from drf_spectacular.utils import extend_schema, extend_schema_view
77
from rest_framework import mixins, permissions, status
8+
from rest_framework.authentication import get_authorization_header
89
from rest_framework.decorators import action
910
from rest_framework.exceptions import NotAuthenticated
1011
from rest_framework.parsers import FormParser, JSONParser
@@ -249,6 +250,12 @@ def create(self, request, *args, **kwargs):
249250
user = get_database_user(request.user)
250251
username = user.username
251252

253+
# Return 401 if no authentication provided and there are no files,
254+
# for digest authentication to work properly
255+
has_auth = bool(get_authorization_header(request))
256+
if not has_auth and not (bool(request.FILES) or bool(request.data)):
257+
raise NotAuthenticated
258+
252259
if request.method.upper() == 'HEAD':
253260
return Response(
254261
status=status.HTTP_204_NO_CONTENT,

0 commit comments

Comments
 (0)