Esempio n. 1
0
 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)
Esempio n. 2
0
    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
Esempio n. 3
0
 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],
     )
Esempio n. 4
0
    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()
Esempio n. 5
0
 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
Esempio n. 6
0
    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)
Esempio n. 7
0
 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)
Esempio n. 8
0
 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
Esempio n. 9
0
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
Esempio n. 10
0
    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
Esempio n. 11
0
 def perform_create(self, serializer):
     user = get_database_user(self.request.user)
     serializer.save(owner=user)
Esempio n. 12
0
 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'],
     )
Esempio n. 13
0
    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
Esempio n. 14
0
 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()]