Skip to content

Commit

Permalink
Fix #1028 -- Add permission check to GeoOSK submissions
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverroick committed Jan 11, 2017
1 parent 952fbfd commit 5e9f9d2
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 10 deletions.
32 changes: 29 additions & 3 deletions cadasta/xforms/mixins/model_helper.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.core.exceptions import ValidationError
from django.core.exceptions import ValidationError, PermissionDenied
from django.core.files.storage import get_storage_class
from django.db import transaction
from django.utils.translation import ugettext as _
from jsonattrs.models import Attribute, AttributeType
from tutelary.models import Policy
from party.models import Party, TenureRelationship, TenureRelationshipType
from pyxform.xform2json import XFormToDict
from questionnaires.models import Questionnaire
Expand All @@ -13,14 +14,39 @@
from xforms.utils import odk_geom_to_wkt


def get_policy_instance(policy_name, variables=None):
return (Policy.objects.get(name=policy_name), variables)


class ModelHelper():
def __init__(self, *arg):
self.arg = arg

def create_models(self, data):
def _check_perm(self, user, project):
superuser = get_policy_instance('superuser')
org_admin = get_policy_instance('org-admin', {
'organization': project.organization.slug
})
prj_manager = get_policy_instance('project-manager', {
'organization': project.organization.slug,
'project': project.slug
})
data_collector = get_policy_instance('data-collector', {
'organization': project.organization.slug,
'project': project.slug
})

roles = [superuser, org_admin, prj_manager, data_collector]
assigned_policies = user.assigned_policies()
if not any(role in assigned_policies for role in roles):
raise PermissionDenied(_("You don't have permission do contribute"
" data to this project."))

def create_models(self, data, user):
questionnaire = self._get_questionnaire(
id_string=data['id'], version=data['version']
)
self._check_perm(user, questionnaire.project)

# If xform has already been submitted, check for additional resources
additional_resources = self.check_for_duplicate_submission(
Expand Down Expand Up @@ -238,7 +264,7 @@ def upload_submission_data(self, request):
parties, party_resources,
locations, location_resources,
tenure_relationships, tenure_resources
) = self.create_models(submission)
) = self.create_models(submission, request.user)

party_submissions = [submission]
location_submissions = [submission]
Expand Down
25 changes: 23 additions & 2 deletions cadasta/xforms/tests/test_model_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest
from django.conf import settings
from django.test import TestCase
from django.core.exceptions import ValidationError
from django.core.exceptions import ValidationError, PermissionDenied
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.contrib.contenttypes.models import ContentType
from jsonattrs.models import Attribute, AttributeType, Schema
Expand Down Expand Up @@ -118,10 +118,16 @@ def test_create_models(self):
'tenure_resource_photo': 'resource_three.png'
}

user = UserFactory.create()
OrganizationRole.objects.create(
user=user, organization=self.project.organization, admin=True)

(questionnaire,
parties, party_resources,
locations, location_resources,
tenure_relationships, tenure_resources) = mh.create_models(mh(), data)
tenure_relationships, tenure_resources) = mh.create_models(mh(),
data,
user)

assert questionnaire == self.questionnaire
party = Party.objects.get(name='Party One')
Expand Down Expand Up @@ -963,3 +969,18 @@ def test_get_resource_names(self):

assert 'Tenure Resource Thing' not in resources['resources']
assert 'Party Type' not in resources['resources']

def test_check_perm(self):
with pytest.raises(PermissionDenied):
mh._check_perm(mh, self.user, self.project)

org_role = OrganizationRole.objects.get(
user=self.user,
organization=self.project.organization)
org_role.admin = True
org_role.save()

try:
mh._check_perm(mh, self.user, self.project)
except PermissionDenied:
self.fail("PermissionDenied raised unexpectedly")
10 changes: 10 additions & 0 deletions cadasta/xforms/tests/test_views_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ def test_anonymous_user(self):
content_type='multipart/form-data')
assert response.status_code == 403

def test_unauthorized_user(self):
self._create_questionnaire('t_questionnaire', 0)
data = self._submission(form='submission')
user = UserFactory.create()
response = self.request(method='POST', post_data=data, user=user,
content_type='multipart/form-data')
assert response.status_code == 403
assert ("You don't have permission do contribute data to this project."
in response.content)

def test_questionnaire_not_found(self):
with pytest.raises(ValidationError):
data = self._submission(form='submission_bad_questionnaire')
Expand Down
12 changes: 7 additions & 5 deletions cadasta/xforms/views/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from django.utils.six import BytesIO
from django.utils.translation import ugettext as _
Expand Down Expand Up @@ -30,8 +31,7 @@
"""


class XFormSubmissionViewSet(OpenRosaHeadersMixin,
viewsets.GenericViewSet):
class XFormSubmissionViewSet(OpenRosaHeadersMixin, viewsets.GenericViewSet):
"""
Serves up the /collect/submissions/ api requests.
Expand All @@ -53,7 +53,9 @@ def create(self, request, *args, **kwargs):
instance = ModelHelper().upload_submission_data(request)
except InvalidXMLSubmission as e:
logger.debug(str(e))
return self._sendErrorResponse(request, e)
return self._sendErrorResponse(request, e, 'HTTP_400_BAD_REQUEST')
except PermissionDenied as e:
return self._sendErrorResponse(request, e, 'HTTP_403_FORBIDDEN')

# If an already existing XFormSummission is sent back
# don't create another.
Expand Down Expand Up @@ -86,12 +88,12 @@ def create(self, request, *args, **kwargs):
content_type='application/xml'
)

def _sendErrorResponse(self, request, e):
def _sendErrorResponse(self, request, e, code):
message = _(OPEN_ROSA_ENVELOPE.format(message=str(e)))
headers = self.get_openrosa_headers(
request, location=False, content_length=False)
return Response(
message, status=status.HTTP_400_BAD_REQUEST,
message, status=getattr(status, code),
headers=headers, content_type='application/xml'
)

Expand Down

0 comments on commit 5e9f9d2

Please sign in to comment.