def get_queryset(self): user = get_database_user(self.request.user) criteria = {'user': user} if 'asset__uid' in self.request.query_params: criteria['asset__uid'] = self.request.query_params[ 'asset__uid'] return UserAssetSubscription.objects.filter(**criteria)
def create(self, validated_data: dict) -> ExportTask: # Create a new export task user = get_database_user(self._get_request.user) export_task = ExportTask.objects.create(user=user, data=validated_data) # Have Celery run the export in the background export_in_background.delay(export_task_uid=export_task.uid) return export_task
def get_queryset(self, *args, **kwargs): user = get_database_user(self.request.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 remove_perm(self, user_obj, perm, defer_recalc=False, skip_kc=False): """ Revoke the given `perm` on this object from `user_obj`. By default, recalculate descendant objects' permissions and remove any applicable KC permissions. May delete granted permissions or add deny permissions as appropriate: Current access Action ============== ====== None None Direct Remove direct permission Inherited Add deny permission Direct & Inherited Remove direct permission; add deny permission :type user_obj: :py:class:`User` or :py:class:`AnonymousUser` :param perm str: The `codename` of the `Permission` :param defer_recalc bool: When `True`, skip recalculating descendants :param skip_kc bool: When `True`, skip assignment of applicable KC permissions """ user_obj = get_database_user(user_obj) app_label, codename = perm_parse(perm, self) # Get all assignable permissions, regardless of asset type. That way, # we can allow invalid permissions to be removed if codename not in self.get_assignable_permissions(ignore_type=True): # Some permissions are calculated and not stored in the database raise serializers.ValidationError( {'permission': f'{codename} cannot be removed explicitly.'}) all_permissions = self.permissions.filter( user=user_obj, permission__codename=codename, deny=False) direct_permissions = all_permissions.filter(inherited=False) inherited_permissions = all_permissions.filter(inherited=True) # Resolve implied permissions, e.g. revoking view implies revoking # change implied_perms = self.get_implied_perms(codename, reverse=True, for_instance=self) for implied_perm in implied_perms: self.remove_perm(user_obj, implied_perm, defer_recalc=True) # Delete directly assigned permissions, if any direct_permissions.delete() if inherited_permissions.exists(): # Delete inherited permissions inherited_permissions.delete() # Add a deny permission to block future inheritance self.assign_perm(user_obj, perm, deny=True, defer_recalc=True) # Remove any applicable KC permissions if not skip_kc: remove_applicable_kc_permissions(self, user_obj, codename) # We might have been called by ourself to assign a related # permission. In that case, don't recalculate here. if defer_recalc: return self._update_partial_permissions(user_obj, perm, remove=True) # Recalculate all descendants self.recalculate_descendants_perms()
def get_group_permissions(self, user_obj, obj=None): is_anonymous = is_user_anonymous(user_obj) user_obj = get_database_user(user_obj) permissions = super().get_group_permissions(user_obj, obj) if is_anonymous: # Obey limits on anonymous users' permissions allowed_set = set(settings.ALLOWED_ANONYMOUS_PERMISSIONS) return permissions.intersection(allowed_set) else: return permissions
def data(self, request, *args, **kwargs): AVAILABLE_FORMATS = ('csv', 'xlsx') # Serve content directly to these agents instead of redirecting BAD_USER_AGENTS = [ # LibreOffice Calc only refreshes the URL to which it was # redirected (at least until you quit and restart it) r'^LibreOffice', # Microsoft Excel and Power BI fail to send any `Authorization` # headers after a 302 redirect, making authentication fail r'^Microsoft.Data.Mashup', ] format_type = kwargs.get('format', request.GET.get('format')) if format_type not in AVAILABLE_FORMATS: raise serializers.ValidationError( t('Only the following formats are available: ##format list##'). replace('##format list##', ', '.join(AVAILABLE_FORMATS))) user = get_database_user(self.request.user) settings_obj = self.get_object() # formpack is expected to behave properly even if the export settings # were originally created for a different format settings_obj.export_settings['type'] = format_type export = SynchronousExport.generate_or_return_existing( user=user, asset_export_settings=settings_obj, ) if export.status != export.COMPLETE: # The problem has already been logged by `ImportExportTask.run()`, # but pass some information of dubious usefulness back to the # client. return HttpResponse( 'Synchronous export failed: ' + str(export.messages), content_type='text/plain', status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) bad_user_agent = False user_agent = request.META.get('HTTP_USER_AGENT') if user_agent: for ua_pattern in BAD_USER_AGENTS: if re.match(ua_pattern, user_agent): bad_user_agent = True break if bad_user_agent: return FileResponse(export.result.file) file_location = serializers.FileField().to_representation( export.result) return HttpResponseRedirect(file_location)
def has_perm(self, user_obj, perm, obj=None): is_anonymous = is_user_anonymous(user_obj) user_obj = get_database_user(user_obj) if obj is None or not hasattr(obj, 'has_perm'): if is_anonymous: # Obey limits on anonymous users' permissions if perm not in settings.ALLOWED_ANONYMOUS_PERMISSIONS: return False return super().has_perm(user_obj, perm, obj) if not user_obj.is_active: # Inactive users are denied immediately return False # Trust the object-level test to handle anonymous users correctly return obj.has_perm(user_obj, perm)
def has_perm(self, user_obj: User, perm: str) -> bool: """ Does `user_obj` have perm on this object? (True/False) """ app_label, codename = perm_parse(perm, self) is_anonymous = is_user_anonymous(user_obj) user_obj = get_database_user(user_obj) # Treat superusers the way django.contrib.auth does if user_obj.is_active and user_obj.is_superuser: return True # Look for matching permissions result = len( self._get_effective_perms(user=user_obj, codename=codename)) == 1 if not result and not is_anonymous: # The user-specific test failed, but does the public have access? result = self.has_perm(AnonymousUser(), perm) if result and is_anonymous: # Is an anonymous user allowed to have this permission? fq_permission = '{}.{}'.format(app_label, codename) if fq_permission not in settings.ALLOWED_ANONYMOUS_PERMISSIONS: return False return result
def superuser_or_username_matches_prefix(private_file): """ You can create a custom function, and use that instead. The function receives a private_storate.models.PrivateFile object, which has the following fields: request: the Django request. storage: the storage engine used to retrieve the file. relative_name: the file name in the storage. full_path: the full file system path. exists(): whether the file exists. content_type: the HTTP content type. (See https://github.com/edoburu/django-private-storage) """ user = private_file.request.user if not user.is_authenticated: # Try all the DRF authentication methods before giving up request = DRFRequest( private_file.request, authenticators=[ auth() for auth in api_settings.DEFAULT_AUTHENTICATION_CLASSES ], ) user = request.user user = get_database_user(user) if user.is_superuser: return True if private_file.relative_name.startswith( '{}/'.format(user.username) ): return True return False
def validate_parent(self, parent: Asset) -> Asset: user = get_database_user(self.context['request'].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( t('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(t('Target collection not found')) if PERM_CHANGE_ASSET not in parent_perms: raise serializers.ValidationError( t('User cannot update target parent collection')) return parent
def perform_create(self, serializer): user = get_database_user(self.request.user) serializer.save(owner=user)
def get_queryset(self): user = get_database_user(self.request.user) return self.model.objects.filter( user=user, data__source__icontains=self.kwargs['parent_lookup_asset'], )
def assign_perm(self, user_obj, perm, deny=False, defer_recalc=False, skip_kc=False, partial_perms=None): r""" Assign `user_obj` the given `perm` on this object, or break inheritance from a parent object. By default, recalculate descendant objects' permissions and apply any applicable KC permissions. :type user_obj: :py:class:`User` or :py:class:`AnonymousUser` :param perm: str. The `codename` of the `Permission` :param deny: bool. When `True`, break inheritance from parent object :param defer_recalc: bool. When `True`, skip recalculating descendants :param skip_kc: bool. When `True`, skip assignment of applicable KC permissions :param partial_perms: dict. Filters used to narrow down query for partial permissions """ app_label, codename = perm_parse(perm, self) assignable_permissions = self.get_assignable_permissions() if codename not in assignable_permissions: # Some permissions are calculated and not stored in the database raise serializers.ValidationError({ 'permission': f'{codename} cannot be assigned explicitly to {self}' }) is_anonymous = is_user_anonymous(user_obj) user_obj = get_database_user(user_obj) if is_anonymous: # Is an anonymous user allowed to have this permission? fq_permission = f'{app_label}.{codename}' if (not deny and fq_permission not in settings.ALLOWED_ANONYMOUS_PERMISSIONS): raise serializers.ValidationError({ 'permission': f'Anonymous users cannot be granted the permission {codename}.' }) perm_model = Permission.objects.get(content_type__app_label=app_label, codename=codename) existing_perms = self.permissions.filter(user=user_obj) identical_existing_perm = existing_perms.filter( inherited=False, permission_id=perm_model.pk, deny=deny, ) if identical_existing_perm.exists(): # We need to always update partial permissions because # they may have changed even if `perm` is the same. self._update_partial_permissions(user_obj, perm, partial_perms=partial_perms) # The user already has this permission directly applied return identical_existing_perm.first() # Remove any explicitly-defined contradictory grants or denials contradictory_filters = models.Q(user=user_obj, permission_id=perm_model.pk, deny=not deny, inherited=False) if not deny and perm in self.CONTRADICTORY_PERMISSIONS.keys(): contradictory_filters |= models.Q( user=user_obj, permission__codename__in=self.CONTRADICTORY_PERMISSIONS.get( perm), ) contradictory_perms = existing_perms.filter(contradictory_filters) contradictory_codenames = list( contradictory_perms.values_list('permission__codename', flat=True)) contradictory_perms.delete() # Check if any KC permissions should be removed as well if deny and not skip_kc: remove_applicable_kc_permissions(self, user_obj, contradictory_codenames) # Create the new permission new_permission = ObjectPermission.objects.create( asset=self, user=user_obj, permission_id=perm_model.pk, deny=deny, inherited=False) # Assign any applicable KC permissions if not deny and not skip_kc: assign_applicable_kc_permissions(self, user_obj, codename) # Resolve implied permissions, e.g. granting change implies granting # view implied_perms = self.get_implied_perms( codename, reverse=deny, for_instance=self).intersection(assignable_permissions) for implied_perm in implied_perms: self.assign_perm(user_obj, implied_perm, deny=deny, defer_recalc=True) # We might have been called by ourselves to assign a related # permission. In that case, don't recalculate here. if defer_recalc: return new_permission self._update_partial_permissions(user_obj, perm, partial_perms=partial_perms) # Recalculate all descendants self.recalculate_descendants_perms() return new_permission
def _get_assets(self, obj): request = self.context.get('request', None) user = get_database_user(request.user) return [reverse('asset-detail', args=(sa.uid,), request=request) for sa in Asset.objects.filter(tags=obj, owner=user).all()]