I'm trying to write a site in Django where the API URLs are the same as user-facing URLs. But I'm having trouble with pages which use POST requests and CSRF protection. For example, if I have a page /foo/add I want to be able to send POST requests to it in two ways:
As an end user (authenticated using a session cookie) submitting a form. This requires CSRF protection.
As an API client (authenticated using a HTTP request header). This will fail if CSRF protection is enabled.
I have found various ways of disabling CSRF, such as #csrf_exempt, but these all disable it for the entire view. Is there any way of enabling/disabling it at a more fine-grained level? Or am I just going to have to implement by own CSRF protection from scratch?
Modify urls.py
If you manage your routes in urls.py, you can wrap your desired routes with csrf_exempt() to exclude them from the CSRF verification middleware.
for instance,
from django.views.decorators.csrf import csrf_exempt
urlpatterns = patterns(
# ...
# Will exclude `/api/v1/test` from CSRF
url(r'^api/v1/test', csrf_exempt(TestApiHandler.as_view()))
# ...
)
Alternatively, as a Decorator
Some may find the use of the #csrf_exempt decorator more suitable for their needs
for instance,
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
There is a section of Django's CSRF Protection documentation titled View needs protection for one path which describes a solution. The idea is to use #csrf_exempt on the whole view, but when the API client header is not present or invalid, then call a function
annotated with #csrf_protect.
If you are you using class base view (CBV) and want to use the csrf_exempt decorator you will need to use the method decorator.
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
#method_decorator(csrf_exempt, name='dispatch')
class MyView(View):
def post(self, request):
pass # my view code here
In my case, I am using JWT authentication plus csrf_token for some views. And for some reasons that I am unaware of, csrf_exempt does not work when I set it as a decorator or when I wrap the view name in the url patterns.
So here's what I ended up doing. I overrided the initialize_request available in the APIView class.
class ClasssName(views.APIView):
def initialize_request(self, request, *args, **kwargs):
setattr(request, 'csrf_processing_done', True)
return super().initialize_request(request, *args, **kwargs)
Related
I have various applications in a Django project, but I only want users who are logged in to be able to access those pages. How can I restrict access to every pages except the login page which is my main page. For instance, mywebsite.com/home/user should be only available to user and if someone types in that it should redirect them to mywebsite.com
Currently I have two apps, main and Home, I am using ClassBased views on my Home app how can I restrict access to all my pages except login page and show a message as well?
I want to create a template that users can see other user profile details but not change or edit them. How can I do those above steps
Thanks in advance!
According to Docs you can decorate the class based views with #login_required
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
#method_decorator(login_required, name='dispatch')
class ClassBasedView(View):
...
...
Since you are using class based view, you need to add method decorator, else you can use #logine_required directly.
And the other part in the question is again a separate one from this.
You can try This, In very simple way
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
return HttpResponse()
using #login_required means user have to login to access that view
Or If you you Want to use class then try this
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class RestrictedView(LoginRequiredMixin, TemplateView):
template_name = 'foo/restricted.html'
raise_exception = True
permission_denied_message = "You are not allowed here."
I have such api method:
#api_view(['POST'])
#login_required
def get_posts(request):
# ...
How can I disable CSRF only on this method?
For function based views you can usually use the decorator csrf_exempt:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
Update: There may be an exception for the DRF. Take a look here.
I have an admin mixin that I'm using to prevent caching and make sure users are logged in by overriding dispatch(). It's being used in my class-based views.
# mixins.py
from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
class AdminPageMixin(object):
#method_decorator(never_cache)
#method_decorator(staff_member_required)
def dispatch(self, request, *args, **kwargs):
return super(AdminPageMixin, self).dispatch(request, *args, **kwargs)
# views.py
class SomeAdminView(AdminPageMixin, ListView):
I'm running into a problem when I'm trying to run unit tests against SomeAdminView. Yes, I know I can use django's test client to login, but I'm trying to stay away from writing functional tests. I'd like, instead, to wrap AdminPageMixin functionality into a single decorator and call that decorator in urls.py, like so:
url(r'^myurl/$', decorator_wrapper(SomeAdminView.as_view()), name='some-admin-view'),
Alternatively, I could do this:
url(r'^myurl/$', never_cache(staff_member_required(SomeAdminView.as_view())), name='some-admin-view'),
but if I wanted to add a third or forth decorator, I'd be updating a lot of lines in urls.py and repeating a lot of code.
Any ideas how to create this decorator wrapper?
It is quite against the spirit of the CBVs to use decorators in the URLconfs. Instead, use mixins to add the functionality directly to the dispatch method of the view class.
Is there a way to get swagger to use the login_required decorator? I have it on several views:
from django.contrib.auth.decorators import login_required
#login_required
def index(request):
pass
Is there a way I can get swagger to use this as well? It automatically will redirect users to the login page and would be handy to have in place.
I've added this decorator to one of my views
#permission_required('codename')
When a user visits that page and doesn't have the required permissions, he is redirected the login page, but it doesn't really tell him why he's been redirected there. Worse yet, if he logs in, and still doesn't have the permissions, he's going to be completely clueless as to why he can't access that page!
Isn't there a way I can tap into the messages framework and post an error at the same time?
Not sure what version of Django you are using, but in Django 1.4 and higher you can use:
from django.contrib.auth.decorators import permission_required
#permission_required('app.permission',raise_exception=True)
def myView(request):
#your view code
This will raise a 403 exception and if you have a 403.html page at the base of your template folder it will server this out.
If you are using class based views:
from django.views.generic.base import View
from django.contrib.auth.decorators import permission_required
from django.utils.decorators import method_decorator
class MyView(View):
#method_decorator(permission_required('app.permission',raise_exception=True)
def get(self, request):
#your GET view
Hope this helps.
You can tap into the messages framework and provide an error message. See my answer to an identical question.
You could use login_url parameter in this decorator to redirect to some other page, rather than login page. Or you can simply write your own decorator based on the code from django:
def permission_required(perm, login_url=None):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
Simply change login_url to some redirect_to and it won't cause any confusion.
Use #permission_required_or_403('codename')
This will redirect the users to a 403 'permission denied' error page.