xle
xle

Reputation: 310

In django, how can I ensure that a specific user is accessing a view?

I would like to be able to check if the user accessing a page is a specific user.

For instance, when a user accesses the "Edit Post" page on my blog app, I want to ensure that the user is the author of the post.

Currently, I check that the user accessing '/Blog/Edit//' has the blog.change_post permission.

However, if a second user (who also has that permission) were to enter the URL to change someone else's post, they would pass that permission check and be able to edit someone else's post.

What I want is a @user_passes_test function that check's the user object accessing the view against the author attribute of the post.

#/Blog/urls.py

urlpatterns = [
    ...
    path('Edit/<int:pk>', views.BlogEdit, name='BlogEdit'),
    ...
]



#/Blog/views.py

@permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk)
    if request.method == "POST":
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            post.save()
            return redirect('/Blog', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'Blog/Edit.html', {'form': form})

Upvotes: 4

Views: 1519

Answers (3)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476547

You can add an extra filter to your get_object_or_404:

@permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
    post = get_object_or_404(Post, pk=pk, author=request.user)
    if request.method == "POST":
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            form.save()
            return redirect('/Blog', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'Blog/Edit.html', {'form': form})

Here author is the hypothetical ForeignKey from Post to the user model. It is possible that the name is different, but the idea is still the same.

This thus means that in case the pk is the pk of a Blog for which request.user is not the author, then we will have a 404 response.

The advantage of filtering here, is that we use a single query for the filtering. We will not (lazily) load the author to check if it is the same as the logged in user.

Note: post = form.save(commit=False) and post.save() are equivalent to post = form.save() (so with commit=True).

Upvotes: 6

xle
xle

Reputation: 310

I figured it out from This Post in the end. What urbanspaceman suggests works for class based views, but for function based views you can do:

@permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
    post = get_object_or_404(Post, pk=pk)

    if post.author != request.user:
        raise Http404("You are not allowed to edit this Post")

    # ... Rest of view here ... #

Upvotes: 1

michjnich
michjnich

Reputation: 3385

In a class based view change the get_queryset method to filter by current user.

class MyView(LoginRequiredMixin, UpdateView):
    ...

    def get_queryset(self):
        """
        Only the current logged in user can edit ... 
        """
        return super().get_queryset().filter(created_by=self.request.user)

Upvotes: 1

Related Questions