Pass request.user to dispatch method - django

Env: Python 3.6, Django 3.0, DRF 3.11, JWT Authorization
Hello everybody.
I have a simple class in my API where I want to check user permissions in each method get, post etc. I planned to check user privileges at dispatch but it doesn't work. Simplify code, my current class looks more or less like this:
class ExampleClassName(APIView):
can_do_sth = False
def dispatch(self, request, *args, **kwargs):
print(request.user) # Here is AnonymousUser
if request.user.username == "GreatGatsby":
self.can_do_sth = True
return super(ExampleClassName, self).dispatch(request, *args, **kwargs)
def get(self, request):
print(request.user) # Here is correctly Logged user
if self.can_do_sth:
return Response("You can do it")
return Response("You can't")
def post(self, request):
print(request.user) # Here is correctly Logged user
if self.can_do_sth:
return Response("You can do it")
return Response("You can't")
How can I pass request.user to dispatch method?

ok solved
initialize_request - is doing what I expected. so right dispatch should be:
def dispatch(self, request, *args, **kwargs):
req = self.initialize_request(request, *args, **kwargs)
print(req.user) # Now I have logged user data
if req.user.username == "GreatGatsby":
self.can_do_sth = True
return super(ExampleClassName, self).dispatch(request, *args, **kwargs)
Task closed unless you have any other idea how to do it in different way. If you do - please share :)

Related

Django rest framework non_atomic_requests based on HTTP method

I am using Django Rest Framework and its class-based view, with database connection set to ATOMIC_REQUESTS=True.
To optimize the application, I would like to have it so that only PUT and POST requests are under transaction, but not GET. Since I have ATOMIC_REQUESTS on, the documentation says to use the decorator on the dispatch() method in the view. So this is what I tried:
class MyView(APIView):
def get():
print("I am in a transaction:", not transaction.get_autocommit())
return Response({})
def post():
print("I am in a transaction:", not transaction.get_autocommit())
return Response({})
def put():
print("I am in a transaction:", not transaction.get_autocommit())
return Response({})
def dispatch(self, request, *args, **kwargs):
"""Override dispatch method and selectively apply non_atomic_requests"""
if request.method.lower() == "get":
return self.non_atomic_dispatch(request, *args, **kwargs)
else:
return super().dispatch(request, *args, **kwargs)
#transaction.non_atomic_requests
def non_atomic_dispatch(self, request, *args, **kwargs):
"""Special dispatch method decorated with non_atomic_requests"""
return super().dispatch(request, *args, **kwargs)
This does not work, since the get method still reports itself as under transaction, however, if I switch the decorator to look like this:
...
#transaction.non_atomic_requests
def dispatch(self, request, *args, **kwargs):
if request.method.lower() == "get":
return self.non_atomic_dispatch(request, *args, **kwargs)
else:
return super().dispatch(request, *args, **kwargs)
def non_atomic_dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
...
This works, though obviously it's not what I want.
My questions are:
Why does the placement of the decorator matter, if I am merely returning the same thing?
How do I make this work the way I want?

Limit Django UpdateView to author

I've got a UpdateView in Django that I need to restrict to only the author. I having trouble grabbing the Author off of the request.
class MyPermissionMixin(LoginRequiredMixin, UserPassesTestMixin):
def dispatch(self, request, *args, **kwargs):
user_test_result = self.get_test_func()()
if request.user != ????.user: #How do I grab the user('Author')??
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
Get Autor instance user through self.get_object()
class MyPermissionMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.user != self.get_object().author:
return HttpResponseForbidden()
return super().dispatch(request, *args, **kwargs)

Where to check for 403 in a Django CBV?

I am making a basic app to teach beginners. Each user can write notes, but I want to make it so that a user cannot view or update a different user's notes.
I have the following view, but I had to repeat myself.
from django.core.exceptions import PermissionDenied
...
class NoteUpdate(LoginRequiredMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).post(request, *args, **kwargs)
I feel like there is probably a way to do this without repeating myself. Yeah, I could write a method like this and call it from both:
def check_permission(self):
if self.object.owner != self.request.user:
raise PermissionDenied
But what I really mean is am I overriding the wrong methods? Is there a more traditional way to do this? It feels a little weird overriding .get() and .post()
To answer your question: Overriding .get() and .post() is fine, since for security and integrity reasons, you would want both your get() and post() views to validate before displaying and especially modifying data. Now, if you want to refactor doing this in get or post, there are 2 easy ways of doing this:
Primary (Model Method):
models.py
class Model(models.Model):
owner = models.ForeignKey(User)
...
def deny_if_not_owner(self, user):
if self.owner != user:
raise PermissionDenied
return self.owner
views.py
class NoteUpdate(LoginRequiredMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.deny_if_not_owner(request.user)
return super(NoteUpdate, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
self.object.deny_if_not_owner(request.user)
return super(NoteUpdate, self).post(request, *args, **kwargs)
///////
Alternative (Mixin):
Creating a Mixin would allow you to easily add this code to many classes if you see yourself using this validation again in the future.
class DenyWrongUserMixin(object):
def get(self):
if self.object.owner != self.request.user:
raise PermissionDenied
return super(DenyWrongUserMixin, self).get(*args, **kwargs)
def post(self):
if self.object.owner != self.request.user:
raise PermissionDenied
return super(DenyWrongUserMixin, self).post(*args, **kwargs)
and then:
class NoteUpdate(LoginRequiredMixin, DenyWrongUserMixin, UpdateView):
...
def get(self, request, *args, **kwargs):
...
def post(self, request, *args, **kwargs):
...
You can override the get method or the get_queryset method. The get_queryset will raise a 404 if the logged in user is not the owner.
def get_queryset(self):
qs = super(NoteUpdate, self).get_queryset()
return qs.filter(owner=self.request.user)
or you can just override the get method since it will be called first and then raise PermissionDenied, so no reason to override the post method as well.
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.owner != self.request.user:
raise PermissionDenied
return super(NoteUpdate, self).get(request, *args, **kwargs)
Then you can create a mixin and extend your views from the mixin to avoid the duplication.

FormView validation on fields not from form

I am writing a FormView which will add for example a comment on Person object.
I want to check if current user wrote a comment for this Person and if he did I'd like to raise Http404.
Question: What is the best place for this validation? I don't want to call validation in get_context_data and form_valid. Is dispatch method a good place for this logic?
Remember that form_valid will only be called when you POST the form so that won't work as GET requests will still render. You could therefore put it in the get method for the FormView which would prevent the view and template loading the initial form. The drawback is that people could technically still POST to that URL if they really wanted to.
As you mentioned, I would put it in the dispatch method. It is very early in the cycle of the FormView so you avoid unnecessary processing.
def dispatch(self, request, *args, **kwargs):
# I'm just guessing your Comment/Person models
user = self.request.user
try:
person = Person.objects.get(user=self.request.user)
except:
raise Http404("No user exists")
if Comment.objects.filter(content_object=person).exist():
raise Http404("Comment already exists")
return super(MyFormView, self).dispatch(request, *args, **kwargs)
EDIT This is a good link describing the various ways you can decorate your class based view to add permission and user checking
I wrote this mixin
class PermissionCheckMixin(object):
def __init__(self, perm=None, obj=None):
self.perm = perm
self.obj = obj
def dispatch(self, request, *args, **kwargs):
if request.user.is_anonymous():
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
elif request.user.is_authenticated():
if self.perm:
if request.user.has_perm(self.perm, self.obj):
return super(PermissionCheckMixin, self).dispatch(request, *args, **kwargs)
else:
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
else:
if request.is_ajax():
return JSONResponseForbidden()
else:
return HttpResponseForbidden()
And use it like this:
class TestFormView(PermissionCheckMixin, FormView):
...
You can easily adapt this mixin, somehow like this:
def __init__(self, pk):
self.person_pk = pk
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
if request.user.pk == self.person_pk:
return HttpResponseNotFound()

Django Admin: Getting request in ModelForm's constructor

The below code removes certain values from a drop down menu.
It works fine but I want to remove the value if the user lacks certain permissions.
How can I access request.user in the ModelForm's constructor? Or is there a better way to accomplish what I am trying to do?
class AnnouncementModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AnnouncementModelForm, self).__init__(*args, **kwargs)
self.fields["category"].queryset = AnnouncementCategory.objects.filter(can_post=True)
How can I access request.user in the ModelForm's constructor?
To use it in the Form constructor, just pass the request to it.
class AnnouncementModelForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(AnnouncementModelForm, self).__init__(*args, **kwargs)
qs = request.user.foreignkeytable__set.all()
self.fields["category"].queryset = qs
Ok here is how I solved it:
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "category" and not request.user.has_perm('can_post_to_all'):
kwargs["queryset"] = AnnouncementCategory.objects.filter(can_post=True)
return db_field.formfield(**kwargs)
return super(AnnouncementAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)