diff --git a/onadata/apps/api/tests/viewsets/test_xform_submission_viewset.py b/onadata/apps/api/tests/viewsets/test_xform_submission_viewset.py index 45fd324580..e8d49ed0a4 100644 --- a/onadata/apps/api/tests/viewsets/test_xform_submission_viewset.py +++ b/onadata/apps/api/tests/viewsets/test_xform_submission_viewset.py @@ -2,6 +2,7 @@ """ Test XFormSubmissionViewSet module. """ + import os from builtins import open # pylint: disable=redefined-builtin from unittest.mock import patch @@ -11,6 +12,8 @@ from django.core.files.uploadedfile import InMemoryUploadedFile from django.http import UnreadablePostError from django.test import TransactionTestCase +from django.urls.exceptions import NoReverseMatch +from rest_framework.reverse import reverse import simplejson as json from django_digest.test import DigestAuth @@ -843,9 +846,7 @@ def test_floip_format_multiple_rows_instance(self): data_responses = [i[4] for i in json.loads(data)] self.assertTrue(any(i in data_responses for i in instance_json.values())) - @patch( - "onadata.apps.api.viewsets.xform_submission_viewset.SubmissionSerializer" - ) # noqa + @patch("onadata.apps.api.viewsets.xform_submission_viewset.SubmissionSerializer") # noqa def test_post_submission_unreadable_post_error(self, MockSerializer): """ Test UnreadablePostError exception during submission.. @@ -1232,6 +1233,55 @@ def test_post_submission_using_project_pk_while_authenticated(self): Instance.objects.filter(xform=self.xform).count(), count + 1 ) + def test_post_submission_using_project_pk_exceptions(self): + """ + Test that one is able to submit data using the project + submission endpoint built for a particular form through + it's primary key while authenticated + """ + with self.assertRaises(NoReverseMatch): + _url = reverse("submission", kwargs={"project_id": "mission"}) + s = self.surveys[0] + media_file = "1335783522563.jpg" + path = os.path.join( + self.main_directory, + "fixtures", + "transportation", + "instances", + s, + media_file, + ) + with open(path, "rb") as f: + f = InMemoryUploadedFile( + f, "media_file", media_file, "image/jpg", os.path.getsize(path), None + ) + submission_path = os.path.join( + self.main_directory, + "fixtures", + "transportation", + "instances", + s, + s + ".xml", + ) + with open(submission_path, "rb") as sf: + data = {"xml_submission_file": sf, "media_file": f} + count = Instance.objects.filter(xform=self.xform).count() + request = self.factory.post( + f"/projects/{self.xform.project.pk}hello/submission", data + ) + response = self.view(request) + self.assertEqual(response.status_code, 401) + auth = DigestAuth("bob", "bobbob") + request.META.update(auth(request.META, response)) + response = self.view(request, project_pk=f"{self.project.pk}hello") + self.assertEqual(response.status_code, 400) + self.assertTrue(response.has_header("X-OpenRosa-Version")) + self.assertTrue(response.has_header("X-OpenRosa-Accept-Content-Length")) + self.assertTrue(response.has_header("Date")) + self.assertEqual( + Instance.objects.filter(xform=self.xform).count(), count + ) + @patch.object(ServiceDefinition, "send") def test_new_submission_sent_to_rapidpro(self, mock_send): """Submission created is sent to RapidPro""" diff --git a/onadata/apps/main/urls.py b/onadata/apps/main/urls.py index 79002beeb4..e2ab2ce830 100644 --- a/onadata/apps/main/urls.py +++ b/onadata/apps/main/urls.py @@ -2,6 +2,7 @@ """ URLs path. """ + import sys import django @@ -203,7 +204,7 @@ name="view-submission-list", ), re_path( - r"^forms/(?P\w+)/view/submissionList$", + r"^forms/(?P\d+)/view/submissionList$", BriefcaseViewset.as_view({"get": "list", "head": "list"}), name="view-submission-list", ), @@ -218,7 +219,7 @@ name="view-download-submission", ), re_path( - r"^forms/(?P\w+)/view/downloadSubmission$", + r"^forms/(?P\d+)/view/downloadSubmission$", BriefcaseViewset.as_view({"get": "retrieve", "head": "retrieve"}), name="view-download-submission", ), @@ -377,17 +378,17 @@ name="form-list", ), re_path( - r"^enketo/(?P\w+)/formList$", + r"^enketo/(?P\d+)/formList$", XFormListViewSet.as_view({"get": "list", "head": "list"}), name="form-list", ), re_path( - r"^forms/(?P\w+)/formList$", + r"^forms/(?P\d+)/formList$", XFormListViewSet.as_view({"get": "list", "head": "list"}), name="form-list", ), re_path( - r"^enketo-preview/(?P\w+)/formList$", + r"^enketo-preview/(?P\d+)/formList$", PreviewXFormListViewSet.as_view({"get": "list", "head": "list"}), name="form-list", ), @@ -444,17 +445,17 @@ name="submissions", ), re_path( - r"^enketo/(?P\w+)/submission$", + r"^enketo/(?P\d+)/submission$", XFormSubmissionViewSet.as_view({"post": "create", "head": "create"}), name="submissions", ), re_path( - r"^projects/(?P\w+)/submission$", + r"^projects/(?P\d+)/submission$", XFormSubmissionViewSet.as_view({"post": "create", "head": "create"}), name="submissions", ), re_path( - r"^forms/(?P\w+)/submission$", + r"^forms/(?P\d+)/submission$", XFormSubmissionViewSet.as_view({"post": "create", "head": "create"}), name="submissions", ), diff --git a/onadata/libs/serializers/data_serializer.py b/onadata/libs/serializers/data_serializer.py index 3510111e9d..cdd7bc3171 100644 --- a/onadata/libs/serializers/data_serializer.py +++ b/onadata/libs/serializers/data_serializer.py @@ -2,6 +2,7 @@ """ Submission data serializers module. """ + from io import BytesIO from django.shortcuts import get_object_or_404 @@ -13,6 +14,7 @@ from onadata.apps.logger.models import Project, XForm from onadata.apps.logger.models.instance import Instance, InstanceHistory +from onadata.libs.data import parse_int from onadata.libs.serializers.fields.json_field import JsonField from onadata.libs.utils.analytics import TrackObjectEvent from onadata.libs.utils.common_tags import ( @@ -56,11 +58,19 @@ def get_request_and_username(context): # get the username from the XForm object if form_id is # present else utilize the request users username if form_pk: - form = get_object_or_404(XForm, pk=form_pk) - username = form.user.username + form_pk = parse_int(form_pk) + if form_pk: + form = get_object_or_404(XForm, pk=form_pk) + username = form.user.username + else: + raise ValueError(_("Invalid XForm id.")) elif project_pk: - project = get_object_or_404(Project, pk=project_pk) - username = project.user.username + project_pk = parse_int(project_pk) + if project_pk: + project = get_object_or_404(Project, pk=project_pk) + username = project.user.username + else: + raise ValueError(_("Invalid Project id.")) else: username = request.user and request.user.username @@ -296,7 +306,10 @@ def update(self, instance, validated_data): pass def validate(self, attrs): - request, __ = get_request_and_username(self.context) + try: + request, __ = get_request_and_username(self.context) + except ValueError as exc: + raise serializers.ValidationError(str(exc)) if not request.FILES or "xml_submission_file" not in request.FILES: raise serializers.ValidationError(_("No XML submission file."))