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
def test_viewers_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 # `anotheruser` must see only permissions assigned to themselves, the # owner (`self.admin`) and the anonymous user. Permissions assigned to # `someuser` must not appear assignable_perms = self.asset.get_assignable_permissions() expected_perms = [] for user in [self.admin, self.anotheruser, 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 _get_subscribed(cls, user): # Of the public objects, determine to which the user has subscribed if is_user_anonymous(user): user = get_anonymous_user() return UserAssetSubscription.objects.filter( asset__in=cls._get_publics(), user=user).values('asset')
def filter_queryset(self, request, queryset, view): # TODO: omit objects for which the user has only a deny permission user = request.user if isinstance(request.user, AnonymousUser): user = get_anonymous_user() if user.is_superuser: # Superuser sees all return queryset if user.pk == settings.ANONYMOUS_USER_ID: # Hide permissions from anonymous users return queryset.none() """ A regular user sees their own permissions and the owner's permissions for objects to which they have access. For example, if Alana and John have view access to an object owned by Richard, John should see all of his own permissions and Richard's permissions, but not any of Alana's permissions. """ result = ObjectPermission.objects.filter( Q(asset__owner=user) # owner sees everything | Q(user=user) # everyone with access sees their own | Q( # everyone with access sees the owner's asset__permissions__user=user, user=F('asset__owner'))).distinct() return result
def _get_queryset_for_discoverable_child_assets( self, request: Request, queryset: QuerySet) -> QuerySet: """ Returns a queryset containing the children of publically discoverable assets based on the discoverability of the child's parent. The parent uid is passed in the request query string. """ self._return_queryset = False parameters = self.__get_parsed_parameters(request) try: parent_uids = parameters[self.PARENT_UID_PARAMETER] except KeyError: return queryset # `self.__get_parsed_parameters()` returns a list for each parameters # but we should only search only with one parent uid parent_obj = queryset.get(uid=parent_uids[0]) if not isinstance(parent_obj, Asset): return queryset if parent_obj.has_perm(get_anonymous_user(), PERM_DISCOVER_ASSET): self._return_queryset = True return queryset.filter(pk__in=self._get_publics(), parent=parent_obj) return queryset
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)
def test_different_sort_between_python_and_db(self): # Ensure that `AnonymousUser` is created to include it in the list below get_anonymous_user() User.objects.bulk_create([ User(first_name='A', last_name='User', username='******'), User(first_name='Alexander', last_name='Mtembenuzeni', username='******'), User(first_name='Another', last_name='User', username='******'), ]) users = list( User.objects.filter(username__istartswith='a').values_list( 'username', flat=True).order_by('username')) # The database (PostgreSQL, as of Jun, 14, 2022) seems to be case # insensitive and treats `_` after any letters. # Python is case sensitive and treats `_` before any letters. expected_database = [ 'alex_Mtemb', 'AnonymousUser', 'anotheruser', 'a_user', ] expected_python = [ 'AnonymousUser', 'a_user', 'alex_Mtemb', 'anotheruser', ] self.assertEqual(users, expected_database) self.assertEqual(sorted(users), expected_python) # Obviously if the first two assertions are True, the one below should # be false. No matter what, let's be paranoid and test it anyway. self.assertNotEqual(users, sorted(users))
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)
def test_synchronous_csv_export_anonymous_with_permission(self): self.asset.assign_perm(get_anonymous_user(), PERM_VIEW_SUBMISSIONS) es = self._create_export_settings() synchronous_exports_url = reverse( self._get_endpoint('asset-export-settings-synchronous-data'), kwargs={ 'parent_lookup_asset': self.asset.uid, 'uid': es.uid, 'format': 'csv', }, ) response = self.client.get(synchronous_exports_url, follow=True) assert response.status_code == status.HTTP_200_OK
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')
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 test_export_task_list_anon_public_asset(self): # make submissions public self.asset.assign_perm(get_anonymous_user(), PERM_VIEW_SUBMISSIONS) for _type in ['csv', 'xls', 'spss_labels']: self._create_export_task(_type=_type) self.client.logout() list_url = reverse( self._get_endpoint('asset-export-list'), kwargs={ 'format': 'json', 'parent_lookup_asset': self.asset.uid }, ) response = self.client.get(list_url) assert response.status_code == status.HTTP_200_OK data = response.json() # should not list any results as exports were created by another user assert not data['results']
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 test_create_export_anon(self): anon = get_anonymous_user() self.asset.assign_perm(anon, PERM_VIEW_SUBMISSIONS) self._create_export_task(_type='xls', user=self.user) self.client.logout() self._create_export_task(_type='xls', user=anon) list_url = reverse( self._get_endpoint('asset-export-list'), kwargs={ 'format': 'json', 'parent_lookup_asset': self.asset.uid }, ) response = self.client.get(list_url) assert response.status_code == status.HTTP_200_OK data = response.json() # two total exports on asset, but only one by anon assert len(data['results']) == 1 download_url = data['results'][0]['result'] download_response = self.client.get(download_url) assert download_response.status_code == status.HTTP_200_OK
def _get_discoverable(self, queryset): # We were asked not to consider subscriptions; return all # discoverable objects return get_objects_for_user(get_anonymous_user(), PERM_DISCOVER_ASSET, queryset)
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)
def _get_publics(): view_asset_perm_id = get_perm_ids_from_code_names(PERM_VIEW_ASSET) return ObjectPermission.objects.filter( deny=False, user=get_anonymous_user(), permission_id=view_asset_perm_id).values('asset')
from kpi.deployment_backends.kobocat_backend import KobocatDeploymentBackend from kpi.models import Asset, ObjectPermission from kpi.utils.object_permission import get_anonymous_user from kpi.utils.models 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(KobocatXForm) 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
def test_no_assignments_saved_on_error(self): # Call `get_anonymous_user()` to create AnonymousUser if it does not exist get_anonymous_user() # Ensure someuser and anotheruser do not have 'view_submissions' on `self.asset` self.assertFalse( self.asset.has_perm(self.someuser, PERM_VIEW_SUBMISSIONS)) self.assertFalse( self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS)) # Allow someuser and anotheruser to view submissions good_assignments = [{ 'user': '******', 'permission': PERM_VIEW_SUBMISSIONS, }, { 'user': '******', 'permission': PERM_VIEW_SUBMISSIONS, }] assignments = self.translate_usernames_and_codenames_to_urls( good_assignments) bulk_endpoint = reverse( self._get_endpoint('asset-permission-assignment-bulk-assignments'), kwargs={'parent_lookup_asset': self.asset.uid}) response = self.client.post(bulk_endpoint, assignments, format='json') # Everything worked as expected, someuser and anotheruser got 'view_submissions' self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertTrue( self.asset.has_perm(self.someuser, PERM_VIEW_SUBMISSIONS)) self.assertTrue( self.asset.has_perm(self.anotheruser, PERM_VIEW_SUBMISSIONS)) # but do not have respectively 'delete_submissions' and 'change_submissions' self.assertFalse( self.asset.has_perm(self.someuser, PERM_DELETE_SUBMISSIONS)) self.assertFalse( self.asset.has_perm(self.anotheruser, PERM_CHANGE_SUBMISSIONS)) bad_assignments = [ { 'user': '******', 'permission': PERM_ADD_SUBMISSIONS, # should return a 400 }, { 'user': '******', 'permission': PERM_DELETE_SUBMISSIONS, }, { 'user': '******', 'permission': PERM_CHANGE_SUBMISSIONS, } ] assignments = self.translate_usernames_and_codenames_to_urls( bad_assignments) bulk_endpoint = reverse( self._get_endpoint('asset-permission-assignment-bulk-assignments'), kwargs={'parent_lookup_asset': self.asset.uid}) response = self.client.post(bulk_endpoint, assignments, format='json') # Could not assign 'add_submissions' to anonymous user. self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) # Ensure that someuser and anotheruser did not get any other permissions # than the one they already had, i.e.: 'view_submissions'. self.assertFalse( self.asset.has_perm(self.someuser, PERM_DELETE_SUBMISSIONS)) self.assertFalse( self.asset.has_perm(self.anotheruser, PERM_CHANGE_SUBMISSIONS))
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['asset'].queryset = get_objects_for_user( get_anonymous_user(), [PERM_VIEW_ASSET, PERM_DISCOVER_ASSET], Asset)