Beispiel #1
0
    def patch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        # permission check deferred to .perform_update()
        instance = self.model_object
        has_read_permissions = False
        if p.has_perms_shortcut(self.user_object, instance, "r"):
            has_read_permissions = True

        serializer = self.get_serializer(
            instance=instance,
            data=self.request_data,
            partial=True,
        )
        serializer.is_valid(raise_exception=True)
        # User must have write permission on the field being modified.
        for field_name, field_val in serializer.validated_data.items():
            # Note that in case field_name is a foreign key, there are two
            # cases:
            #   1. brand
            #   2. brand_id
            # If the field_name is brand then field_val is usually a brand
            # object. If the field name is brand_id, then field val is UUID.
            field = self.get_model_field(field_name)
            if not field:
                continue
            # For foreign keys, user must have 3-way permissions. See
            # .check_3way_permissions().
            if isinstance(field, ForeignKey):
                field_name = field.name  # this removes "_id"
                if isinstance(field_val, DCFModel):
                    field_val_pk = field_val.pk
                else:
                    field_val_pk = field_val
                new_related_obj: QuerySet = QuerySet(model=field.related_model).filter(
                    pk=field_val_pk
                )
                if not new_related_obj.exists():
                    raise e.NotFound(f"Related object {field_val_pk} does not exist.")
                self.check_3way_permissions(
                    self.user_object,
                    self.model_object,
                    field_name,
                    new_related_obj,
                    "w",
                )
            elif not p.has_perms_shortcut(
                self.user_object, self.model_object, "w", field_name
            ):
                raise APIPermissionDenied(self.model_object, "w", field=field_name)
        # when permited
        serializer.save()

        if has_read_permissions:
            p.add_perms_shortcut(self.user_object, instance, "r")
        if p.has_perms_shortcut(self.user_object, instance, "r"):
            return Response(
                self.get_serializer(instance=instance).data,
                status=200,
            )
        else:
            raise UpdatedHiddenObject()
 def get(self, request: HttpRequest, *args: Any,
         **kwargs: Any) -> HttpResponse:
     """Handles GET request."""
     if not p.has_perms_shortcut(self.user_object, self.model_object, "r",
                                 self.field_name):
         raise APIPermissionDenied(self.model_object, "r", self.field_name)
     if self.is_related_object_api:
         if self.field_val:
             if not p.has_perms_shortcut(self.user_object, self.field_val,
                                         "r"):
                 raise APIPermissionDenied(self.field_val, "r")
             serializer = self.get_serializer(instance=self.field_val)
             return Response(serializer.data)
         else:
             raise e.NotFound()
     else:
         queryset = self.filter_queryset(self.queryset)
         assert self.paginator
         page: Iterable[Serializable] = self.paginator.paginate_queryset(
             queryset, self.request, view=self)
         serializer = self.get_serializer()
         return self.paginator.get_paginated_response([
             data.json(
                 version=self.version,
                 context=self.get_serializer_context(),
                 serializer=serializer,
             ) for data in page
         ])
    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        if not p.has_perms_shortcut(self.user_object, self.model.as_model_type(), "c"):
            raise e.PermissionDenied("You have no permission to perform POST.")
        serializer = self.get_serializer(data=self.request_data)
        serializer.is_valid(raise_exception=True)
        # Make sure user has related field's write permission for each related
        # object
        for field_name, field_value in serializer.validated_data.items():
            field = self.get_model_field(field_name)
            if field and isinstance(field, ForeignKey) and field_value:
                # Note that in case field_name is a foreign key, there are two
                # cases:
                #   1. brand
                #   2. brand_id
                # If the field_name is brand then field_val is usually a brand
                # object. If the field name is brand_id, then field val is UUID.
                new_related_obj: None | DCFModel
                if isinstance(field_value, DCFModel):
                    new_related_obj = field_value
                else:
                    new_related_obj = (
                        QuerySet(model=field.related_model)
                        .filter(pk=field_value)
                        .first()
                    )
                if new_related_obj is None:
                    raise e.NotFound(f"Related object {field_value} does not exist.")
                else:
                    # Make sure the related object's related name can be
                    # written. For example, for product/<id>/brand, this is
                    # checking if brand.products can be written.
                    if not p.has_perms_shortcut(
                        self.user_object,
                        new_related_obj,
                        "w",
                        field_name=field.remote_field.name,
                    ):
                        raise APIPermissionDenied(
                            new_related_obj, "w", field=field.remote_field.name
                        )

        instance = serializer.save()
        if p.has_perms_shortcut(self.user_object, instance, "r"):
            return Response(
                self.get_serializer(instance).data,
                status=201,
            )
        else:
            raise CreatedHiddenObject()
Beispiel #4
0
 def delete(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
     if not p.has_perms_shortcut(self.user_object, self.model_object, "d"):
         raise APIPermissionDenied(self.model_object, "d")
     try:
         serializer = self.get_serializer(self.model_object, data=self.request_data)
         if hasattr(serializer, "delete_obj"):
             serializer.delete_obj()
         else:
             self.model_object.delete()
     except ProtectedError as excpt:
         raise e.ValidationError(str(excpt))
     else:
         return Response(status=204)
 def __return_get_result_if_permitted(self, request: HttpRequest,
                                      *args: Any,
                                      **kwargs: Any) -> HttpResponse:
     """Checks for the user's read permission of the current route's model
     object. If no read permission, Responses with error, otherwise handles
     as a GET request.
     """
     if p.has_perms_shortcut(self.user_object,
                             self.model_object,
                             "r",
                             field_name=self.field_name):
         return self.get(request, *args, **kwargs)
     else:
         raise ActionSuccessHiddenObject()
    def check_3way_permissions(
        cls,
        user: DCFAbstractUser,
        model_object: Model,
        field_name: str,
        new_vals: QuerySet,
        perms: str,
    ) -> None:
        """Check user's permission on three things:

        1. The object's field (field_name)
        2. Each old related object (field on the reverse side)
        3. Each new related object (field on the reverse side)
        """

        field = cls._get_field(model_object._meta.model, field_name)
        assert isinstance(
            field,
            (
                ManyToManyRel,
                ManyToManyField,
                ManyToOneRel,
                ForeignKey,
            ),
        )

        if not p.has_perms_shortcut(
                user,
                model_object,
                perms,
                field_name=field_name,
        ):
            raise APIPermissionDenied(model_object, perms, field=field_name)

        old_vals = cls._get_field_as_queryset(model_object, field_name)
        diff_remove = old_vals.difference(new_vals)
        diff_add = new_vals.difference(old_vals)
        # Django doesn't support calling .filter() after .union() and
        # .difference()
        symmetric_diff_pks = diff_add.union(diff_remove).values_list("pk")
        diff_queryset: QuerySet = QuerySet(model=field.related_model).filter(
            pk__in=symmetric_diff_pks)

        cls.__check_perms_for_each(
            user,
            diff_queryset,
            field_name=field.remote_field.name,
            perms=perms,
        )
Beispiel #7
0
 def get(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
     if not p.has_perms_shortcut(self.user_object, self.model_object, "r"):
         raise APIPermissionDenied(self.model_object, "r")
     serializer = self.get_serializer(instance=self.model_object)
     return Response(serializer.data)
Beispiel #8
0
 def test_object_perm_does_not_imply_model_perm(self) -> None:
     add_perms_shortcut(self.user, self.product, "r")
     self.assertFalse(has_perms_shortcut(self.user, Product, "r"))
Beispiel #9
0
 def test_field_perm_does_not_imply_object_perm(self) -> None:
     add_perms_shortcut(self.user, self.product, "r", field_name="barcode")
     self.assertFalse(has_perms_shortcut(self.user, self.product, "r"))
Beispiel #10
0
 def test_model_perm_implies_object_perm_for_field(self) -> None:
     add_perms_shortcut(self.user, Product, "r", "brand")
     self.assertTrue(has_perms_shortcut(self.user, self.product, "r", "brand"))
Beispiel #11
0
 def test_object_perm_implies_field_perm_for_field(self) -> None:
     product = Product.objects.create()
     add_perms_shortcut(self.user, product, "r", "brand")
     self.assertTrue(has_perms_shortcut(self.user, product, "r", "brand"))
Beispiel #12
0
 def test_assign_field(self) -> None:
     self.assertFalse(has_perms_shortcut(self.user, self.product, "r", "brand"))
     add_perms_shortcut(self.user, self.product, "r")
     self.assertTrue(has_perms_shortcut(self.user, self.product, "r", "brand"))