Allow only the owners of the parent model to create a child model when utilising generic views (django-guardian)

369 Views Asked by At

Currently, I have two models, Parent and Child, with a one-to-many relationship. I am using the built-in generic class-based views for CRUD upon a Parent, where I'm using a django-guardian mixin to prevent users who do not own the Parent object from doing these operations, which works well. However, I want to be able to add Children to a Parent. This works fine using a generic CreateView and a modelform, where the pk of the parent is a kwarg passed in the url. However if the user changes the pk in the URL to another user's Parent object's pk, they can add a Child object to it. I want to use django-guardian (or some other means) to prevent a user from adding a Child to another User's Parent object. Can this be done or must it be done some other way? I have got it working by validating the Parent object belongs to the current user within get_form_kwargs in the CreateView but this seems hacky, and would prefer django-guardian.

(I am also not using the 'pk' kwarg in production, and instead using a different 'uuid' kwarg, but I'd like to fix this security hole nonetheless).

Code:

models.py

class Parent(models.Model): 
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True) 
    name = models.CharField(max_length=200, null=True, blank=True)
    
    class Meta:
        permissions = (('manipulate', 'Manipulate'))

class Child(models.Model):
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
    name = models.CharField(max_length=200, null=True, blank=True)

views.py

class CreateChildForm(ModelForm):
    class Meta:
        fields = ('name', 'parent')
        model = models.Child

    def __init__(self, *args, **kwargs):
        self.parent = kwargs.pop('parent')
        super().__init__(*args, **kwargs)
        self.fields['parent'].initial = self.parent
        self.fields['parent'].widget = HiddenInput()

class CreateChild(LoginRequiredMixin, CreateView):
    model = models.Child
    form_class = CreateChildForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        try:
            self.parent = Parent.objects.get(uuid=self.kwargs['uuid'], user=self.request.user)
        except:
            raise PermissionDenied()

        kwargs = super().get_form_kwargs()
        kwargs['parent'] = self.parent
        return kwargs

    def form_valid(self, form):
        form.instance.parent = self.parent
        return super().form_valid(form)
0

There are 0 best solutions below