Exemple #1
0
    def test_editors_see_only_self_anon_and_owner_assignments(self):

        self.client.login(username='******', password='******')
        permission_list_response = self.client.get(
            self.get_asset_perm_assignment_list_url(self.asset), format='json')
        self.assertEqual(permission_list_response.status_code,
                         status.HTTP_200_OK)
        results = permission_list_response.data

        assignable_perms = Asset.get_assignable_permissions()
        expected_perms = []
        for user in [
                self.admin,
                self.someuser,
                # Permissions assigned to self.anotheruser must not appear
                get_anonymous_user(),
        ]:
            user_perms = self.asset.get_perms(user)
            expected_perms.extend(
                (user.username, perm)
                for perm in set(user_perms).intersection(assignable_perms))
        expected_perms = sorted(expected_perms,
                                key=lambda element: (element[0], element[1]))

        obj_perms = []
        for assignment in results:
            object_permission = self.url_to_obj(assignment.get('url'))
            obj_perms.append((
                object_permission.user.username,
                object_permission.permission.codename,
            ))
        obj_perms = sorted(obj_perms,
                           key=lambda element: (element[0], element[1]))

        self.assertEqual(expected_perms, obj_perms)
    def test_anonymous_get_only_owner_s_assignments(self):

        self.client.logout()
        self.collection.assign_perm(get_anonymous_user(), PERM_VIEW_COLLECTION)
        permission_list_response = self.client.get(
            self.collection_permissions_list_url, format='json')
        self.assertEqual(permission_list_response.status_code,
                         status.HTTP_200_OK)
        admin_perms = self.collection.get_perms(self.admin)
        results = permission_list_response.data

        # As an editor of the collection. `someuser` should see all.
        expected_perms = []
        for admin_perm in admin_perms:
            if admin_perm in Collection.get_assignable_permissions():
                expected_perms.append((self.admin.username, admin_perm))

        expected_perms = sorted(expected_perms,
                                key=lambda element: (element[0], element[1]))
        obj_perms = []
        for assignment in results:
            object_permission = self.url_to_obj(assignment.get('url'))
            obj_perms.append((object_permission.user.username,
                              object_permission.permission.codename))

        obj_perms = sorted(obj_perms,
                           key=lambda element: (element[0], element[1]))
        self.assertEqual(expected_perms, obj_perms)
Exemple #3
0
 def test_list_submissions_anonymous_asset_publicly_shared(self):
     self.client.logout()
     anonymous_user = get_anonymous_user()
     self.asset.assign_perm(anonymous_user, 'view_submissions')
     response = self.client.get(self.submission_url, {"format": "json"})
     self.assertEqual(response.status_code, status.HTTP_200_OK)
     self.asset.remove_perm(anonymous_user, 'view_submissions')
Exemple #4
0
    def validate_parent(self, parent: Asset) -> Asset:
        request = self.context['request']
        user = request.user
        if user.is_anonymous:
            user = get_anonymous_user()

        # Validate first if user can update the current parent
        if self.instance and self.instance.parent is not None:
            if not self.instance.parent.has_perm(user, PERM_CHANGE_ASSET):
                raise serializers.ValidationError(
                    _('User cannot update current parent collection'))

        # Target collection is `None`, no need to check permissions
        if parent is None:
            return parent

        # `user` must have write access to target parent before being able to
        # move the asset.
        parent_perms = parent.get_perms(user)
        if PERM_VIEW_ASSET not in parent_perms:
            raise serializers.ValidationError(_('Target collection not found'))

        if PERM_CHANGE_ASSET not in parent_perms:
            raise serializers.ValidationError(
                _('User cannot update target parent collection'))

        return parent
Exemple #5
0
    def test_anonymous_get_only_owner_and_anonymous_assignments(self):

        self.client.logout()
        permission_list_response = self.client.get(
            self.get_asset_perm_assignment_list_url(self.asset), format='json'
        )
        self.assertEqual(permission_list_response.status_code, status.HTTP_200_OK)
        admin = self.admin
        admin_perms = self.asset.get_perms(admin)
        anon = get_anonymous_user()
        anon_perms = self.asset.get_perms(anon)
        assignable_perms = self.asset.get_assignable_permissions()
        results = permission_list_response.data

        # Get admin permissions.
        expected_perms = []
        for user, perms in [(anon, anon_perms), (admin, admin_perms)]:
            for perm in perms:
                if perm in assignable_perms:
                    expected_perms.append((user.username, perm))

        expected_perms = sorted(expected_perms, key=lambda element: (element[0],
                                                                     element[1]))
        obj_perms = []
        for assignment in results:
            object_permission = self.url_to_obj(assignment.get('url'))
            obj_perms.append((object_permission.user.username,
                              object_permission.permission.codename))

        obj_perms = sorted(obj_perms, key=lambda element: (element[0],
                                                           element[1]))
        self.assertEqual(expected_perms, obj_perms)
Exemple #6
0
    def get_queryset(self):
        queryset = Asset.objects.filter(asset_type=ASSET_TYPE_SURVEY)
        if self.action == 'retrieve':
            # `get_object()` will do the checking; no need to manipulate the
            # queryset further
            return queryset.defer('content')

        # `ReportsListSerializer` needs only the UID; don't bother retrieving
        # anything else from the database
        queryset = queryset.only('uid')

        # Reduce the number of asset versions we have to consider by filtering
        # for accessible assets first
        owned_and_explicitly_shared = get_objects_for_user(
            self.request.user,
            self.required_permissions,
            queryset,
            all_perms_required=False,
        )
        subscribed_and_public = get_objects_for_user(
            get_anonymous_user(),
            self.required_permissions,
            queryset.filter(
                parent__userassetsubscription__user=self.request.user
            ),
            all_perms_required=False,
        )

        # Find which of these are deployed, using a custom manager method
        deployed_assets = (
            owned_and_explicitly_shared | subscribed_and_public
        ) & Asset.objects.deployed().distinct()

        return deployed_assets
Exemple #7
0
    def get_queryset(self, *args, **kwargs):
        user = self.request.user
        # Check if the user is anonymous. The
        # django.contrib.auth.models.AnonymousUser object doesn't work for
        # queries.
        if user.is_anonymous:
            user = get_anonymous_user()

        def _get_tags_on_items(content_type_name, avail_items):
            """
            return all ids of tags which are tagged to items of the given
            content_type
            """
            same_content_type = Q(
                taggit_taggeditem_items__content_type__model=content_type_name)
            same_id = Q(taggit_taggeditem_items__object_id__in=avail_items.
                        values_list('id'))
            return Tag.objects.filter(same_content_type & same_id).distinct().\
                values_list('id', flat=True)

        accessible_collections = get_objects_for_user(user,
                                                      PERM_VIEW_COLLECTION,
                                                      Collection).only('pk')
        accessible_assets = get_objects_for_user(user, PERM_VIEW_ASSET,
                                                 Asset).only('pk')
        all_tag_ids = list(
            chain(
                _get_tags_on_items('collection', accessible_collections),
                _get_tags_on_items('asset', accessible_assets),
            ))

        return Tag.objects.filter(id__in=all_tag_ids).distinct()
 def perform_create(self, serializer):
     # Check if the user is anonymous. The
     # django.contrib.auth.models.AnonymousUser object doesn't work for
     # queries.
     user = self.request.user
     if user.is_anonymous:
         user = get_anonymous_user()
     serializer.save(owner=user)
    def test_anotheruser_can_export_when_submissions_publicly_shared(self):
        """
        Running through behaviour described in issue kpi/#2870 where an asset
        that has been publicly shared and then explicity shared with a user, the
        user has lower permissions than an anonymous user and is therefore
        unable to export submission data.
        """
        # resetting permissions of `anotheruser` to have no permissions
        self.asset.remove_perm(self.anotheruser, PERM_PARTIAL_SUBMISSIONS)
        self.asset.remove_perm(self.anotheruser, PERM_VIEW_ASSET)

        anonymous_user = get_anonymous_user()

        assert self.asset.has_perm(self.anotheruser, PERM_VIEW_ASSET) == False
        assert PERM_VIEW_ASSET not in self.asset.get_perms(self.anotheruser)
        assert self.asset.has_perm(self.anotheruser, PERM_CHANGE_ASSET) == False
        assert PERM_CHANGE_ASSET not in self.asset.get_perms(self.anotheruser)

        # required to export
        self.asset.assign_perm(self.anotheruser, PERM_CHANGE_ASSET)

        assert self.asset.has_perm(self.anotheruser, PERM_VIEW_ASSET) == True
        assert PERM_VIEW_ASSET in self.asset.get_perms(self.anotheruser)
        assert self.asset.has_perm(self.anotheruser, PERM_CHANGE_ASSET) == True
        assert PERM_CHANGE_ASSET in self.asset.get_perms(self.anotheruser)

        assert (
            self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS)
            == False
        )
        assert PERM_VIEW_SUBMISSIONS not in self.asset.get_perms(
            self.anotheruser
        )

        self.asset.assign_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)

        assert (
            self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS) == True
        )
        assert PERM_VIEW_SUBMISSIONS in self.asset.get_perms(self.anotheruser)

        # testing anotheruser can export data
        self.run_csv_export_test(user=self.anotheruser)

        # resetting permissions of asset
        partial_perms = {
            PERM_VIEW_SUBMISSIONS: [
                {'_submitted_by': self.anotheruser.username}
            ]
        }
        self.asset.assign_perm(
            self.anotheruser,
            PERM_PARTIAL_SUBMISSIONS,
            partial_perms=partial_perms,
        )
        self.asset.remove_perm(self.anotheruser, PERM_CHANGE_ASSET)
        self.asset.remove_perm(anonymous_user, PERM_VIEW_ASSET)
        self.asset.remove_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)
Exemple #10
0
    def get_queryset(self):
        
        # Retrieve all deployed assets first.
        deployed_assets = Asset.objects.filter(asset_versions__deployed=True).distinct()
        # Then retrieve all assets user is allowed to view (user must have 'view_submissions' on Asset objects)
        user_assets = get_objects_for_user(self.request.user, 'view_submissions', deployed_assets)
        publicly_shared_assets = get_objects_for_user(get_anonymous_user(), 'view_submissions', deployed_assets)

        return user_assets | publicly_shared_assets
Exemple #11
0
 def _get_assets(self, obj):
     request = self.context.get('request', None)
     user = request.user
     # Check if the user is anonymous. The
     # django.contrib.auth.models.AnonymousUser object doesn't work for
     # queries.
     if user.is_anonymous:
         user = get_anonymous_user()
     return [reverse('asset-detail', args=(sa.uid,), request=request)
             for sa in Asset.objects.filter(tags=obj, owner=user).all()]
 def get_queryset(self):
     user = self.request.user
     # Check if the user is anonymous. The
     # django.contrib.auth.models.AnonymousUser object doesn't work for
     # queries.
     if user.is_anonymous:
         user = get_anonymous_user()
     criteria = {'user': user}
     if 'collection__uid' in self.request.query_params:
         criteria['collection__uid'] = self.request.query_params[
             'collection__uid']
     return UserCollectionSubscription.objects.filter(**criteria)
Exemple #13
0
    def setUp(self):
        super().setUp()
        self.anon = get_anonymous_user()
        self.super = User.objects.get(username='******')
        self.super_password = '******'
        self.someuser = User.objects.get(username='******')
        self.someuser_password = '******'
        self.anotheruser = User.objects.get(username='******')
        self.anotheruser_password = '******'

        self.collection = Asset.objects.create(
            asset_type=ASSET_TYPE_COLLECTION, owner=self.someuser)
        self.asset = Asset.objects.create(owner=self.someuser)
Exemple #14
0
 def user_matches(self, user, ignore_invalid_queries=True):
     if user.is_anonymous():
         user = get_anonymous_user()
     manager = user._meta.model.objects
     queryset = manager.none()
     for user_query in self.user_queries:
         try:
             queryset |= manager.filter(**user_query)
         except (FieldError, TypeError):
             if ignore_invalid_queries:
                 return False
             else:
                 raise
     return queryset.filter(pk=user.pk).exists()
Exemple #15
0
 def has_object_permission(self, request, view, obj):
     # Checks if the user has the require permissions
     # To access the submission data in reports
     user = request.user
     if user.is_superuser:
         return True
     if user.is_anonymous:
         user = get_anonymous_user()
     permissions = list(obj.get_perms(user))
     required_permissions = [
         PERM_VIEW_SUBMISSIONS,
         PERM_PARTIAL_SUBMISSIONS,
     ]
     return any(perm in permissions for perm in required_permissions)
Exemple #16
0
    def get_queryset(self, *args, **kwargs):
        user = self.request.user
        # Check if the user is anonymous. The
        # django.contrib.auth.models.AnonymousUser object doesn't work for
        # queries.
        if user.is_anonymous:
            user = get_anonymous_user()

        accessible_asset_pks = get_objects_for_user(user, PERM_VIEW_ASSET,
                                                    Asset).only('pk')
        content_type = ContentType.objects.get_for_model(Asset)
        return Tag.objects.filter(
            taggit_taggeditem_items__content_type=content_type,
            taggit_taggeditem_items__object_id__in=[accessible_asset_pks],
        )
    def setUp(self):
        KpiTestCase.setUp(self)

        self.anon = get_anonymous_user()
        self.admin = User.objects.get(username='******')
        self.admin_password = '******'
        self.someuser = User.objects.get(username='******')
        self.someuser_password = '******'

        self.login(self.admin.username, self.admin_password)
        self.admins_public_asset = self.create_asset('admins_public_asset')
        self.add_perm(self.admins_public_asset, self.anon, 'view')

        self.login(self.someuser.username, self.someuser_password)
        self.someusers_public_asset = self.create_asset('someusers_public_asset')
        self.add_perm(self.someusers_public_asset, self.anon, 'view')
Exemple #18
0
    def test_list_submissions_asset_publicly_shared_and_shared_with_user(self):
        """
        Running through behaviour described in issue kpi/#2870 where an asset
        that has been publicly shared and then explicity shared with a user, the
        user has lower permissions than an anonymous user and is therefore
        unable to view submission data.
        """

        self._log_in_as_another_user()
        anonymous_user = get_anonymous_user()

        assert self.asset.has_perm(self.anotheruser, PERM_VIEW_ASSET) == False
        assert PERM_VIEW_ASSET not in self.asset.get_perms(self.anotheruser)
        assert self.asset.has_perm(self.anotheruser, PERM_CHANGE_ASSET) == False
        assert PERM_CHANGE_ASSET not in self.asset.get_perms(self.anotheruser)

        self.asset.assign_perm(self.anotheruser, PERM_CHANGE_ASSET)

        assert self.asset.has_perm(self.anotheruser, PERM_VIEW_ASSET) == True
        assert PERM_VIEW_ASSET in self.asset.get_perms(self.anotheruser)
        assert self.asset.has_perm(self.anotheruser, PERM_CHANGE_ASSET) == True
        assert PERM_CHANGE_ASSET in self.asset.get_perms(self.anotheruser)

        assert (
            self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS)
            == False
        )
        assert PERM_VIEW_SUBMISSIONS not in self.asset.get_perms(
            self.anotheruser
        )

        self.asset.assign_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)

        assert self.asset.has_perm(self.anotheruser, PERM_VIEW_ASSET) == True
        assert PERM_VIEW_ASSET in self.asset.get_perms(self.anotheruser)

        assert (
            self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS) == True
        )
        assert PERM_VIEW_SUBMISSIONS in self.asset.get_perms(self.anotheruser)

        # resetting permssions of asset
        self.asset.remove_perm(self.anotheruser, PERM_VIEW_ASSET)
        self.asset.remove_perm(self.anotheruser, PERM_CHANGE_ASSET)
        self.asset.remove_perm(anonymous_user, PERM_VIEW_ASSET)
        self.asset.remove_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)
    def test_list_submissions_authenticated_asset_publicly_shared(self):
        """ https://github.com/kobotoolbox/kpi/issues/2698 """

        anonymous_user = get_anonymous_user()
        self._log_in_as_another_user()

        # Give the user who will access the public data--without any explicit
        # permission assignment--their own asset. This is needed to expose a
        # flaw in `ObjectPermissionMixin.__get_object_permissions()`
        Asset.objects.create(name='i own it', owner=self.anotheruser)

        # `self.asset` is owned by `someuser`; `anotheruser` has no
        # explicitly-granted access to it
        self.asset.assign_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)
        response = self.client.get(self.submission_url, {"format": "json"})
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.asset.remove_perm(anonymous_user, PERM_VIEW_SUBMISSIONS)
Exemple #20
0
    def has_permission(self, request, view):
        if not request.user:
            return False
        elif request.user.is_superuser:
            return True

        parent_object = self._get_parent_object(view)

        user = request.user
        if user.is_anonymous:
            user = get_anonymous_user()

        user_permissions = self._get_user_permissions(parent_object, user)
        view_permissions = self.get_required_permissions('GET')
        can_view = set(view_permissions).issubset(user_permissions)

        try:
            required_permissions = self.get_required_permissions(
                request.method)
        except exceptions.MethodNotAllowed as e:
            # Only reveal the HTTP 405 if the user has view access
            if can_view:
                raise e
            else:
                raise Http404

        if user == parent_object.owner:
            # The owner can always manage permission assignments
            has_perm = True
        else:
            has_perm = set(required_permissions).issubset(user_permissions)

        if has_perm:
            # Access granted!
            return True

        if not has_perm and can_view:
            # If users are allowed to view, we want to show them HTTP 403
            return False

        # Don't reveal the existence of this object to users who do not have
        # permission to view it
        raise Http404
    def setUp(self):
        self.anon = get_anonymous_user()
        self.someuser = User.objects.get(username='******')
        self.someuser_password = '******'

        # This was written when we allowed anons to create assets, but I'll
        # leave it here just to make sure it has no effect
        permission = Permission.objects.get(codename='add_asset')
        self.anon.user_permissions.add(permission)

        # Log in and create an asset that anon can access
        self.client.login(username=self.someuser.username,
                          password=self.someuser_password)
        self.anon_accessible = self.create_asset('Anonymous can access this!')
        self.add_perm(self.anon_accessible, self.anon, 'view_')
        # Log out and become anonymous again
        self.client.logout()
        response = self.client.get(reverse('currentuser-detail'))
        self.assertFalse('username' in response.data)
    def setUp(self):
        super().setUp()
        self.anon = get_anonymous_user()
        self.super = User.objects.get(username='******')
        self.super_password = '******'
        self.someuser = User.objects.get(username='******')
        self.someuser_password = '******'
        self.anotheruser = User.objects.get(username='******')
        self.anotheruser_password = '******'

        def create_object_with_specific_pk(model, pk, **kwargs):
            obj = model()
            obj.pk = pk
            for k, v in kwargs.items():
                setattr(obj, k, v)
            obj.save()
            return obj

        self.collection = Asset.objects.create(
            asset_type=ASSET_TYPE_COLLECTION, owner=self.someuser)
        self.asset = Asset.objects.create(owner=self.someuser)
    def get_queryset(self):

        # Retrieve all deployed assets first.
        deployed_assets = Asset.objects.filter(
            asset_versions__deployed=True).distinct()
        # Then retrieve all assets user is allowed to view
        # (user must have 'view_submissions' on Asset objects)
        required_permissions = [
            PERM_VIEW_SUBMISSIONS,
            PERM_PARTIAL_SUBMISSIONS,
        ]
        user_assets = get_objects_for_user(self.request.user,
                                           required_permissions,
                                           deployed_assets,
                                           all_perms_required=False)
        publicly_shared_assets = get_objects_for_user(get_anonymous_user(),
                                                      required_permissions,
                                                      deployed_assets,
                                                      all_perms_required=False)

        return user_assets | publicly_shared_assets
Exemple #24
0
 def get_fields(self, *args, **kwargs):
     fields = super().get_fields(*args, **kwargs)
     user = self.context['request'].user
     # Check if the user is anonymous. The
     # django.contrib.auth.models.AnonymousUser object doesn't work for
     # queries.
     if user.is_anonymous:
         user = get_anonymous_user()
     if 'parent' in fields:
         # TODO: remove this restriction?
         fields['parent'].queryset = fields['parent'].queryset.filter(
             owner=user)
     # Honor requests to exclude fields
     # TODO: Actually exclude fields from tha database query! DRF grabs
     # all columns, even ones that are never named in `fields`
     excludes = self.context['request'].GET.get('exclude', '')
     for exclude in excludes.split(','):
         exclude = exclude.strip()
         if exclude in fields:
             fields.pop(exclude)
     return fields
    def setUp(self):
        super().setUp()
        self.anon = get_anonymous_user()
        self.super = User.objects.get(username='******')
        self.super_password = '******'
        self.someuser = User.objects.get(username='******')
        self.someuser_password = '******'
        self.anotheruser = User.objects.get(username='******')
        self.anotheruser_password = '******'

        # Find an unused, common PK for both Asset and Collection--useful for
        # catching bugs related to content types like
        # https://github.com/kobotoolbox/kpi/issues/2270
        last_asset = Asset.objects.order_by('pk').last()
        last_collection = Collection.objects.order_by('pk').last()
        available_pk = 1 + max(last_asset.pk if last_asset else 1,
                               last_collection.pk if last_collection else 1)

        def create_object_with_specific_pk(model, pk, **kwargs):
            obj = model()
            obj.pk = pk
            for k, v in kwargs.items():
                setattr(obj, k, v)
            obj.save()
            return obj

        self.collection = create_object_with_specific_pk(
            Collection,
            available_pk,
            owner=self.someuser,
        )
        self.asset = create_object_with_specific_pk(
            Asset,
            available_pk,
            owner=self.someuser,
            # perenially evil `auto_now_add` leaves the field NULL if a pk is
            # specified, leading to `IntegrityError` unless we set it manually
            date_created=timezone.now(),
        )
 def __init__(self, *args, **kwargs):
     super().__init__(*args, **kwargs)
     self.fields['collection'].queryset = get_objects_for_user(
         get_anonymous_user(), PERM_VIEW_COLLECTION,
         Collection.objects.filter(discoverable_when_public=True))
Exemple #27
0
    def setUp(self):
        super().setUp()

        self.asset.assign_perm(self.someuser, PERM_CHANGE_ASSET)
        self.asset.assign_perm(self.anotheruser, PERM_VIEW_ASSET)
        self.asset.assign_perm(get_anonymous_user(), PERM_VIEW_ASSET)
Exemple #28
0
from kpi.deployment_backends.kobocat_backend import KobocatDeploymentBackend
from kpi.models import Asset, ObjectPermission
from kpi.models.object_permission import get_anonymous_user
from kpi.model_utils import _set_auto_field_update

TIMESTAMP_DIFFERENCE_TOLERANCE = datetime.timedelta(seconds=30)

# Swap keys and values so that keys are KC's codenames and values are KPI's
PERMISSIONS_MAP = {kc: kpi for kpi, kc in Asset.KC_PERMISSIONS_MAP.items()}

# Optimization
ASSET_CT = ContentType.objects.get_for_model(Asset)
FROM_KC_ONLY_PERMISSION = Permission.objects.get(
    content_type=ASSET_CT, codename=PERM_FROM_KC_ONLY)
XFORM_CT = ShadowModel.get_content_type_for_model(ReadOnlyKobocatXForm)
ANONYMOUS_USER = get_anonymous_user()
# Replace codenames with Permission PKs, remembering the codenames
permission_map_copy = dict(PERMISSIONS_MAP)

KPI_PKS_TO_CODENAMES = {}
for kc_codename, kpi_codename in permission_map_copy.items():
    kc_perm_pk = KobocatPermission.objects.get(
        content_type=XFORM_CT, codename=kc_codename).pk
    kpi_perm_pk = Permission.objects.get(
        content_type=ASSET_CT, codename=kpi_codename).pk

    del PERMISSIONS_MAP[kc_codename]

    PERMISSIONS_MAP[kc_perm_pk] = kpi_perm_pk
    KPI_PKS_TO_CODENAMES[kpi_perm_pk] = kpi_codename