Izabel Cybulska
Izabel Cybulska

Reputation: 23

How to restrict access users who are not an author to post on django generic editing views?

Building a website with referring to a book, I made a blog application.
And I found even nonauthors can edit/delete the post which they didn't write. So I've tried to fix it in many ways but failed so far.

Here are my codes.

models.py

class Post(models.Model):
    title = models.CharField('TITLE', max_length=50)
    slug = models.SlugField('SLUG', unique=True, allow_unicode=True)
    description = models.CharField('DESCRIPTION', max_length=100, blank=True)
    content = models.TextField('CONTENT')
    create_date = models.DateTimeField('Create Date', auto_now_add=True)
    modify_date = models.DateTimeField('Modify Date', auto_now=True)
    tag = TagField()
    author = models.ForeignKey(User, null=True)

    class Meta:
        ordering = ('-modify_date',) 

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('blog:post_detail', args=(self.slug,))

    def save(self, *args, **kwargs):
        if not self.id:
            self.slug = slugify(self.title, allow_unicode=True)
        super(Post, self).save(*args, **kwargs)

views.py

class PostUpdateView(LoginRequiredMixin, edit.UpdateView):
    model = Post
    fields = ['title', 'slug', 'description', 'content', 'tag']
    success_url = reverse_lazy('blog:index')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super(PostUpdateView, self).form_valid(form)


class PostDeleteView(LoginRequiredMixin, edit.DeleteView):
    model = Post
    success_url = reverse_lazy('blog:index')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super(PostDeleteView, self).form_valid(form)

Below is the only code which worked and I found it in docs(Using FormMixin with DetailView)

def post(self, request, *args, **kwargs):
    if not ((request.user==Post.author) or request.user.is_superuser):
        return HttpResponseForbidden()
    self.object = self.get_object()
    form = self.get_form()
    if form.is_valid():
        return self.form_valid(form)
    else:
        return self.form_invalid(form)

It worked, in some points, cuz it returns HttpResponseForbidden if nonauthor try to click "submit" in update form. However, what I want is that nonauthor cannot even access to update page.

I also found the way to customize dispatch() in stackoverflow but it didn't work. I'm not sure whether it is proper in Django to use {% if not user == object.author %}{% else %}{% endif %} on templates like in PHP. Any advice will be appreciated.

Thanks

Upvotes: 1

Views: 708

Answers (1)

Kevin
Kevin

Reputation: 959

The dispatch method is the right place to do this because it will affect all requests. As you've noticed, putting access control code in the post method doesn't affect GET requests.

How did you try to override dispatch? Because something like this should work:

from django.core.exceptions import PermissionDenied

class PostUpdateView(LoginRequiredMixin, edit.UpdateView):
    #
    # All of your other view code here....
    #
    def dispatch(self, request, *args, **kwargs):
        handler = super().dispatch(request, *args, **kwargs)
        user = request.user
        post = self.get_object()
        if not (post.author == user or user.is_superuser):
            raise PermissionDenied
        return handler

If you're supporting Python2 you'll have to change the super() call to super(PostUpdateView, self).dispatch(request, *args, **kwargs).

Upvotes: 3

Related Questions