Skip to content

Commit 8dccba4

Browse files
authored
Merge pull request #3157 from kobotoolbox/3153-fix-user-in-kobocat-proxy-request
Fix kobocat proxy request user
2 parents 52e0873 + a5e7cf8 commit 8dccba4

File tree

8 files changed

+45
-20
lines changed

8 files changed

+45
-20
lines changed

jsapp/js/actions.es6

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ actions.resources.duplicateSubmission.listen((uid, sid, duplicatedSubmission) =>
539539
actions.resources.loadAsset({id: uid});
540540
})
541541
.fail((response) => {
542-
alertify.error(t('Failed to duplciate submisson'));
542+
alertify.error(t('Failed to duplicate submisson'));
543543
actions.resources.duplicateSubmission.failed(response);
544544
});
545545
});

jsapp/js/components/modalForms/submission.es6

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ class Submission extends React.Component {
457457
}
458458

459459
{this.userCan('change_submissions', this.props.asset) &&
460+
this.userCan('add_submissions', this.props.asset) &&
460461
<a
461462
onClick={this.duplicateSubmission.bind(this)}
462463
className='kobo-button kobo-button--blue submission-duplicate__button'

kpi/deployment_backends/kobocat_backend.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -664,24 +664,24 @@ def format_openrosa_datetime(dt: Optional[datetime] = None) -> str:
664664
return dt.isoformat('T', 'milliseconds')
665665

666666
def duplicate_submission(
667-
self, requesting_user_id: int, instance_id: int
667+
self, requesting_user: 'auth.User', instance_id: int
668668
) -> dict:
669669
"""
670-
Dupicates a single submission proxied through kobocat. The submission
670+
Duplicates a single submission proxied through kobocat. The submission
671671
with the given `instance_id` is duplicated and the `start`, `end` and
672672
`instanceID` parameters of the submission are reset before being posted
673673
to kobocat.
674674
675675
Args:
676-
requesting_user_id (int)
676+
requesting_user ('auth.User')
677677
instance_id (int)
678678
679679
Returns:
680680
dict: message response from kobocat and uuid of created submission
681681
if successful
682682
"""
683683
params = self.validate_submission_list_params(
684-
requesting_user_id,
684+
requesting_user.id,
685685
format_type=INSTANCE_FORMAT_TYPE_XML,
686686
instance_ids=[instance_id],
687687
)
@@ -711,12 +711,12 @@ def duplicate_submission(
711711
method='POST', url=self.submission_url, files=files
712712
)
713713
kc_response = self.__kobocat_proxy_request(
714-
kc_request, user=self.asset.owner
714+
kc_request, user=requesting_user
715715
)
716716

717717
if kc_response.status_code == status.HTTP_201_CREATED:
718718
return next(
719-
self.get_submissions(requesting_user_id, query={'_uuid': _uuid})
719+
self.get_submissions(requesting_user.id, query={'_uuid': _uuid})
720720
)
721721
else:
722722
raise KobocatDuplicateSubmissionException
@@ -775,7 +775,7 @@ def set_validation_statuses(self, data, user, method):
775775
return self.__prepare_as_drf_response_signature(kc_response)
776776

777777
def bulk_update_submissions(
778-
self, request_data: dict, requesting_user_id: int
778+
self, request_data: dict, requesting_user: 'auth.User'
779779
) -> dict:
780780
"""
781781
Allows for bulk updating of submissions proxied through kobocat. A
@@ -788,15 +788,15 @@ def bulk_update_submissions(
788788
Args:
789789
request_data (dict): must contain a list of `submission_ids` and at
790790
least one other key:value field for updating the submissions
791-
requesting_user_id (int)
791+
requesting_user ('auth.User')
792792
793793
Returns:
794794
dict: formatted dict to be passed to a Response object
795795
"""
796796
payload = self.__prepare_bulk_update_payload(request_data)
797797
kwargs = {'instance_ids': payload.pop('submission_ids')}
798798
params = self.validate_submission_list_params(
799-
requesting_user_id, format_type=INSTANCE_FORMAT_TYPE_XML, **kwargs
799+
requesting_user.id, format_type=INSTANCE_FORMAT_TYPE_XML, **kwargs
800800
)
801801
submissions = list(self.__get_submissions_in_xml(**params))
802802
validated_submissions = self.__validate_bulk_update_submissions(
@@ -865,7 +865,7 @@ def bulk_update_submissions(
865865
method='POST', url=self.submission_url, files=files
866866
)
867867
kc_response = self.__kobocat_proxy_request(
868-
kc_request, user=self.asset.owner
868+
kc_request, user=requesting_user
869869
)
870870

871871
kc_responses.append(

kpi/deployment_backends/mock_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def get_submissions(self, requesting_user_id,
207207
return submissions
208208

209209
def duplicate_submission(
210-
self, requesting_user_id: int, instance_id: int, **kwargs: dict
210+
self, requesting_user: 'auth.User', instance_id: int, **kwargs: dict
211211
) -> dict:
212212
# TODO: Make this operate on XML somehow and reuse code from
213213
# KobocatDeploymentBackend, to catch issues like #3054
@@ -268,7 +268,7 @@ def __prepare_bulk_update_response(kc_responses: list) -> dict:
268268
}
269269

270270
def bulk_update_submissions(
271-
self, request_data: dict, requesting_user_id: int
271+
self, request_data: dict, requesting_user: 'auth.User'
272272
) -> dict:
273273
payload = self.__prepare_bulk_update_payload(request_data)
274274
all_submissions = copy.copy(self.asset._deployment_data['submissions'])

kpi/permissions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,10 @@ class EditSubmissionPermission(SubmissionPermission):
262262
class DuplicateSubmissionPermission(SubmissionPermission):
263263
perms_map = {
264264
'GET': ['%(app_label)s.view_%(model_name)s'],
265-
'POST': ['%(app_label)s.change_%(model_name)s'],
265+
'POST': [
266+
'%(app_label)s.add_%(model_name)s',
267+
'%(app_label)s.change_%(model_name)s',
268+
],
266269
}
267270

268271

kpi/tests/api/v2/test_api_submissions.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from rest_framework import status
1212

1313
from kpi.constants import (
14+
PERM_ADD_SUBMISSIONS,
1415
PERM_CHANGE_ASSET,
1516
PERM_CHANGE_SUBMISSIONS,
1617
PERM_DELETE_SUBMISSIONS,
@@ -532,8 +533,23 @@ def test_duplicate_submission_by_anotheruser_shared_view_only_not_allowed(self):
532533
response = self.client.post(self.submission_url, {'format': 'json'})
533534
assert response.status_code == status.HTTP_403_FORBIDDEN
534535

535-
def test_duplicate_submission_by_anotheruser_shared_allowed(self):
536-
self._share_with_another_user(view_only=False)
536+
def test_duplicate_submission_by_anotheruser_shared_change_not_allowed(self):
537+
self.asset.assign_perm(self.anotheruser, PERM_CHANGE_SUBMISSIONS)
538+
self._log_in_as_another_user()
539+
response = self.client.post(self.submission_url, {'format': 'json'})
540+
assert response.status_code == status.HTTP_403_FORBIDDEN
541+
542+
def test_duplicate_submission_by_anotheruser_shared_add_not_allowed(self):
543+
for perm in [PERM_VIEW_SUBMISSIONS, PERM_ADD_SUBMISSIONS]:
544+
self.asset.assign_perm(self.anotheruser, perm)
545+
self._log_in_as_another_user()
546+
response = self.client.post(self.submission_url, {'format': 'json'})
547+
assert response.status_code == status.HTTP_403_FORBIDDEN
548+
549+
def test_duplicate_submission_by_anotheruser_shared_add_and_change_allowed(self):
550+
# user needs both `add_submissions` and `change_submissions` permissions
551+
for perm in [PERM_CHANGE_SUBMISSIONS, PERM_ADD_SUBMISSIONS]:
552+
self.asset.assign_perm(self.anotheruser, perm)
537553
self._log_in_as_another_user()
538554
response = self.client.post(self.submission_url, {'format': 'json'})
539555
assert response.status_code == status.HTTP_201_CREATED

kpi/views/v2/data.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.http import Http404
44
from django.utils.translation import ugettext_lazy as _
55
from rest_framework import (
6+
exceptions,
67
renderers,
78
serializers,
89
status,
@@ -13,7 +14,11 @@
1314
from rest_framework.response import Response
1415
from rest_framework_extensions.mixins import NestedViewSetMixin
1516

16-
from kpi.constants import INSTANCE_FORMAT_TYPE_JSON
17+
from kpi.constants import (
18+
INSTANCE_FORMAT_TYPE_JSON,
19+
PERM_ADD_SUBMISSIONS,
20+
PERM_CHANGE_SUBMISSIONS,
21+
)
1722
from kpi.exceptions import ObjectDeploymentDoesNotExist
1823
from kpi.models import Asset
1924
from kpi.paginators import DataPagination
@@ -287,7 +292,7 @@ def bulk(self, request, *args, **kwargs):
287292
request.user)
288293
elif request.method == 'PATCH':
289294
json_response = deployment.bulk_update_submissions(
290-
dict(request.data), request.user.id
295+
dict(request.data), request.user
291296
)
292297
return Response(**json_response)
293298

@@ -367,7 +372,7 @@ def duplicate(self, request, pk, *args, **kwargs):
367372
"""
368373
deployment = self._get_deployment()
369374
duplicate_response = deployment.duplicate_submission(
370-
requesting_user_id=request.user.id, instance_id=positive_int(pk)
375+
requesting_user=request.user, instance_id=positive_int(pk)
371376
)
372377
return Response(duplicate_response, status=status.HTTP_201_CREATED)
373378

0 commit comments

Comments
 (0)