I've been spending hours trying to find a way to have Basic HTTP Authentication for one of my views. These are several of the solutions I've tried but have had no success. The request is still be processed even with no authentication. I'm using version 1.4.3 of Django. Here is my Django view:
#csrf_exempt
def facebook(request):
if request.user.is_authenticated():
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
else:
return HttpResponse("This user is not authenticated")
I sent in the request without authentication, and it still returned results. This is not suppose to happen.
Another solution I tried was from a Django Snippet I found called, view by view basic authentication decorator
I made a httpauth.py and copied the code over from the snippet:
from mydjangoapp.httpauth import *
#csrf_exempt
#logged_in_or_basicauth()
def facebook(request):
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
I sent the request without authentication, and it still returned results. After exhausting all options, I turned to Django's very own #login_required decorator:
from django.contrib.auth.decorators import login_required
#csrf_exempt
#login_required
def facebook(request):
fb_value= ast.literal_eval(request.body)
queryset = Poster.objects.all().filter(fb_id__in = fb_value.values())
data = serializers.serialize('json', queryset, fields = ('picture','fb_id',))
return HttpResponse(data, 'application/javascript')
Here is more information about my settings.py:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
After trying all of these options, I don't know what to do. Is there anything I am missing here?!
This really isn't much of an answer. I'm sorry that I have to post it here, but the system has cut me off from the comment section.
I don't see any issues with your example with using the #login_required decorator. This is typically how I do it on my Django sites. This leads me to believe that you have 1 of 2 things going on here:
You have a configuration issue in your settings file
During initial testing, you have actually authenticated and created a session.
Again, I don't think your problem is with your code. Please post what you finally determine is the issue so that I (and others) may learn from it.
Related
There are tens of questions that are essentially identical to the one I'm asking. However, none of their answers seem to be working for me.
I have a React front-end where I am using axios to send requests to the back-end. Example
const request = await axios.post('${BASE_URL}/logout/')
Most of the Django Rest Framework endpoints are made with ViewSets. However, I have a few that are custom and mostly made for authentication.
path('createaccount/', views.create_account),
path('me/', views.current_user),
path('logout/', views.logout),
path('login/', views.login),
path('resetpassword', views.reset_password),
For the development of this project I've included #csrf_exempt above these views because I didn't want to deal with it at the time. Now I'm nearing deployment and it's time to figure it out.
Some answers say I need to get a CSRF Token from Django which is stored in cookies and I need to pass that in the header of each request. Some answers say all I need to do is configure axios like
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";
And it will "just work". I've tried adjusting my CSRF_COOKIE_NAME to various values to get this to work too.
Some answers even say to keep #csrf_exempt but that sounds like a very, very bad idea.
Do I actually need to generate/get a CSRF cookie? Do I include it with every request? Or is it just a configuration of axios?
To make CSRF protection work you will need CSRF cookie sent from Django to
React as a response to some request (like login or sth else). It will set cookie using
Set-Cookie on frontend side. So make sure that you have a view that does that on Django side. If not, create a view that as response generates that token.
How Django (4.04) CSRF validation work (simplified, based on middleware/csrf.py):
gets CSRF token from cookie (so frontend needs to resend it back on
another request) - it might also get it from session but in case of
React I would not use it
def _get_token(self, request):
....
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None
Compares that cookie CSRF token with non-cookie token from request:
def _check_token(self, request):
# Access csrf_token via self._get_token() as rotate_token() may have
# been called by an authentication middleware during the
# process_request() phase.
try:
csrf_token = self._get_token(request)
except InvalidTokenFormat as exc:
raise RejectRequest(f"CSRF cookie {exc.reason}.")
if csrf_token is None:
# No CSRF cookie. For POST requests, we insist on a CSRF cookie,
# and in this way we can avoid all CSRF attacks, including login
# CSRF.
raise RejectRequest(REASON_NO_CSRF_COOKIE)
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get("csrfmiddlewaretoken", "")
except UnreadablePostError:
# Handle a broken connection before we've completed reading the
# POST data. process_view shouldn't raise any exceptions, so
# we'll ignore and serve the user a 403 (assuming they're still
# listening, which they probably aren't because of the error).
pass
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX, and
# possible for PUT/DELETE.
try:
request_csrf_token = request.META[settings.CSRF_HEADER_NAME]
except KeyError:
raise RejectRequest(REASON_CSRF_TOKEN_MISSING)
token_source = settings.CSRF_HEADER_NAME
else:
token_source = "POST"
try:
request_csrf_token = _sanitize_token(request_csrf_token)
except InvalidTokenFormat as exc:
reason = self._bad_token_message(exc.reason, token_source)
raise RejectRequest(reason)
if not _does_token_match(request_csrf_token, csrf_token):
reason = self._bad_token_message("incorrect", token_source)
raise RejectRequest(reason)
As you can see you either need to include csrfmiddlewaretoken in POST request or include it in header with key: settings.CSRF_HEADER_NAME and value read from cookies on front-end side.
So for example you set withCredentials: true (to include initial cookie), read that initial CSRF cookie in React and add to header in axios request at specific key.
When in question, I would just debug request setting up breakpoints in this code of Django in middleware/csrf.py and you can trace what is missing and why CSRF validation fails.
I've got this problem once, I was using token authentication. That's how I solved it. But not sure If it is the best idea. I only used csrf_exempt for this view and all others views are viewsets.
#csrf_exempt
def get_current_user(request, *args, **kwargs):
if request.method == 'GET':
user = request.user
serializer = UserDataSerializer(user)
return JsonResponse(serializer.data, safe=False)
My middleware in settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
# 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.locale.LocaleMiddleware',
'oauth2_provider.middleware.OAuth2TokenMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'auditlog.middleware.AuditlogMiddleware',
]
I am using Django Rest's browsable API to POST using Session Authentication, and am getting CSRF token missing, even though it was supplied. I am seeking advice on configuring my ModelViewSet subclass so that this works.
Here's my viewset:
class TreeAPI(ModelViewSet):
authentication_classes = (SessionAuthentication,)
queryset = Tree.objects.get_roots()
parser_classes = (JSONParser, FormParser, MultiPartParser)
permission_classes = (IsAdminUser,)
throttle_classes = (TreeThrottle,)
serializer_class = TreeSerializer
I am able to use the DRF Browsable API to GET this endpoint, but when I use it to POST to this endpoint, I get a 403 with the message CSRF token missing or incorrect.
When I set a breakpoint in the constructor to rest_framework.request.Request, I can see that the request passed in contains the needed csrfmiddleware token:
In Django Rest's Request class, POST is actually a property:
#property
def POST(self):
if not _hasattr(self, '_data'):
self._load_data_and_files()
if is_form_media_type(self.content_type):
# self.data is an empty QueryDict!
return self.data
return QueryDict('', encoding=self._request._encoding)
request.POST no longer contains the csrfmiddlewaretoken key; it is stripped of all keys supplied with the form:
As a result, the parameter passed to rest_framework.authentication.SessionAuthentication.enforce_csrf(request) which is then passed to django.middleware.csrf.CsrfViewMiddleware.process_view does not find the csrfmiddlewaretoken token:
if request.method == "POST":
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
What can I check? What are the possible sources of error here?
Considerations
not interested in disabling CSRF
not interested in using token authentication
am familiar with how to use CSRF tokens and the Django docs on them
am familiar with Django REST's docs on CSRF tokens
this is the built-in Django REST browsable API; haven't modified anything in UI
EDIT 1 - Middleware
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
EDIT 2 - versions of software
- djangorestframework==3.3.3
- Django==1.9.8
EDIT 3 - possibly related issues at the git project
'request.data' empty when multipart form data POST in 3.3.x
3814
Request.data empty when multipart/form-data POSTed
3951
EDIT 4 - possibly related stack overflow posts
- Having a POST'able API and Django's CSRF Middleware
- How to make a POST simple JSON using Django REST Framework? CSRF token missing or incorrect
- How to make a Django-Rest-Framework API that takes POST data?
- Django Rest Framework, ajax POST works but PATCH throws CSRF Failed: CSRF token missing or incorrect
- http://www.django-rest-framework.org/api-guide/parsers/#formparser
This problem does not manifest in djangorestframework==3.5.4. See http://www.django-rest-framework.org/topics/release-notes/; I have a feeling this was fixed after 3.3.x.
I'm trying to work with the following scenario:
When a GET request comes in to my "/" route I normally want to handle it with my HomeView. However, my site is heavy AJAX so if the request's UserAgent is a bot then I serve it up with a fully rendered version of the page (standard PhantomJS stuff). The approach works fine, but the performance of the fully rendered version, and the SLA for that version, is very different than the regular user view. As such, I would like to use a piece of middleware to do the bot detection and based on that middleware I would then like to send the request to a different View.
The middleware part is easy, I have a process_request handler that detects the bot - no big deal. However, I can't figure any option for overriding the View function that will be invoked. Is there a "proper" way to do this in Django? My current thoughts are:
modify request.path_info to change the requested URL so that the router will then send HtmlRendererView rather than HomeView
Call the HtmlRendererView directly from the middleware and return the appropriate HttpResponse. This feels clunky because it then takes away the opportunity for any other middleware to run.
Notes:
I don't want to return a redirect, the crawler is getting a different version of the same resource
I'm on heroku so I can't rewrite the route before it hits Django. If I was using nginx I'd probably just put this logic at that layer and rewrite the URL before it hit Django.
This is not a direct answer to your question ("Reroute a request to a different view"), but maybe this solution could adress your problem.
First, you keep your middleware, but use it only to detect if the visitor is a bot:
def process_request(self, request):
request.is_bot = is_bot(request) # assuming you have a function for detecting bots
return
Then you create a class based view that call a specific method when request.is_bot is True:
class BotViewMixin(object):
def dispatch(self, request, **kwargs):
if request.is_bot:
return self.handle_bot()
return super(BotViewMixin, self).dispatch(request, **kwargs)
You can then inherit this view anywhere you need (e.g. for your Home Page View). You just have to create a handle_bot method on your view, that will return your response for bots.
Advantages of this solution:
You don't need to write different views for bots, just create a dedicated method
You don't block other middlewares
Your logic stay in your views (and not in your middleware)
This is not tested though, so you may need to adapt the code.
EDIT:
Since you use NewRelic and must use a dedicated view for bots in order to get accurate statistics, this approach won't work for you.
You can go with the middleware thing, and still get all middlewares working. You just have to put your own middleware last in MIDDLWARE_CLASSES:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.CrawlerDetector',
)
Also, I think you should write two middlewares methods: process_request for detecting bots, and process_view for redirecting bots to dedicated view.
The following code should probably work for your situation:
from django.core.urlresolvers import reverse
class CrawlerDetector(object):
def process_request(self, request):
"""detect if the user agent is a bot"""
user_agent = request.META.get('HTTP_USER_AGENT', "")
request.is_bot = self.is_crawler(user_agent)
return
def process_view(request, view_func, view_args, view_kwargs):
if request.is_bot and request.path == reverse('home_page'):
return HtmlRendererView().get(request)
return
My current working solution, while not as clean as Eliot's suggested solution, looks (basically) like this:
class CrawlerDetector(object):
# Middleware that detects requests that should be rendered by the HtmlRendererView.
def process_request(self, request):
user_agent = request.META.get('HTTP_USER_AGENT', "")
if not self.is_crawler(user_agent):
return None
return HtmlRendererView().get(request)
It has the downside of removing any downstream middleware from the flow, but it does allow me to call my crawler-specific View before the root View is routed to.
I'm running memcached infront of django without any explicit configuration in my code. I.e. nothing more than
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
...
'django.middleware.cache.FetchFromCacheMiddleware',
)
and
CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
in my settings.py. This works great, in fact so great that it's caching my admin page leaving me no way to moderate live actions on the site until the cache refetches the data. Is there a regex I can throw somewhere to let memcached know to leave my admin page alone? (I'm also using nginx and gunicorn)
You can maybe write your own middleware class or extend from one that django provides.
Specify a dictionary of urls in your settings file:
CACHE_IGNORE_REGEXPS = (
)
Write a Middleware Class:
from django.core.cache import cache
import re
import settings
class MyMemCacheMiddleWare:
def process_response(self, request, response):
cacheIt = True
theUrl = request.get_full_path()
# if it's a GET then store it in the cache:
if request.method != 'GET':
cacheIt = False
# loop on our CACHE_INGORE_REGEXPS and ignore
# certain urls.
for exp in settings.CACHE_IGNORE_REGEXPS:
if re.match(exp,theUrl):
cacheIt = False
if cacheIt:
key = '%s-%s' % (settings.CACHE_KEY_PREFIX,theUrl)
cache.set(key,response.content)
return response
Alex Holt has a pretty neat tutorial about django, memcached and Nginx maybe you wanna read through that to get a better idea.
I had the same exact problem and I came up with an another minimal solution.
First, create your own middleware that inherits the original UpdateCacheMiddleware:
#! /usr/bin/env python
from django.conf import settings
from django.middleware.cache import UpdateCacheMiddleware
class MyUpdateCacheMiddleware(UpdateCacheMiddleware):
def process_response(self, request, response):
full_path = request.get_full_path()
# loop on our CACHE_MIDDLEWARE_IGNORE to ignore certain urls
for ignore in settings.CACHE_MIDDLEWARE_IGNORE:
if ignore.match(full_path):
return response
# ignore patterns missed, pass it to the original middleware
return super(MyUpdateCacheMiddleware, self).process_response(
request, response
)
Then create an ignore regex list in your settings similar to that:
CACHE_MIDDLEWARE_IGNORE = (
re.compile(r'^/admin/'),
)
Now all you need to do is to replace your UpdateCacheMiddleware with the one you created:
MIDDLEWARE_CLASSES = (
'myapp.lib.middlewares.MyUpdateCacheMiddleware',
# ...
'django.middleware.cache.FetchFromCacheMiddleware',
)
Cheers.
I'm trying to write a django middleware using process_template_response but I seems like it's not working, can anybody help me with this or maybe give an example on how to use the method.
below is my code:
class MiddleWare(object):
def process_template_reponse(self, request, response):
response.context_data = dict(title='title')
response.template_name = 'pages/helloworld.html'
return response
in settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'proj.app.middleware.MiddleWare', # here my middleware
)
in template
<!-- nothing is showing -->
{% block title %}{{ title }}{% endblock %}
by the way I'm using Django 1.3 rc 1
Thanks
http://docs.djangoproject.com/en/dev/topics/http/middleware/#process-template-response
Are you using the new SimpleTemplateResponse response classes?
Your view must return SimpleTemplateResponse or subclass for the new process_template_response middleware hook to work.
Copied from the docs:
def my_view(request):
# Create a response
response = TemplateResponse(request, 'mytemplate.html', {})
# Register the callback
response.add_post_render_callback(my_render_callback)
# Return the response
return response
TL;DR: You must render your templates with TemplateResponse:
from django.template.response import TemplateResponse
def myView(request):
context = {'foo':'foo_value'}
return TemplateResponse(request,'foo.html', context)
The problem arises when you render your templates with the good old render imported from django.shorcuts. As stated in the documentation for process_template_response(), this hook gets called when the response is a TemplateResponse or equivalent. So what you should do is to use TemplateResponse to render your templates and your middleware will work.
You can also alias TemplateResponse as render so you don't have to change all your views. I personally don't recommend this.
from django.template.response import TemplateResponse as render
Here is a working example of a simple middleware that uses the process_template_response method.
class ExampleMiddleware(object):
def process_template_response(self, request, response):
response.context_data['title'] = 'We changed the title'
return response
This middleware changes the value of the title variable in the template context data. Install this middleware by adding it to MIDDLEWARE_CLASSES in your settings file. Then visit any page in the django admin app you should see the title of the page change to We changed the title.
I've solved my problem creating a custom template tag. I'm just wondering on how to add a context variable using a process_template_reponse in a middleware.