Django #csrf_exempt decorator not working - python

I'm using DJango 1.8 on a linode server, and have the following view:
import json
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
def home_view(request):
r = { 'response': 'OK', 'data': None }
return HttpResponse(json.dumps(r), content_type = "application/json")
#csrf_exempt
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
return HttpResponse(json.dumps(r), content_type = "application/json")
My urls look like this:
urlpatterns = [
url(r'^django/admin/', include(admin.site.urls)),
url(r'^django/$', home_view),
url(r'^django/ping/$', ping),
url(r'^django/echo/$', echo),
]
If I go to my linode site at
http://mylinodesite/django/ping/
I get:
{"data": "ping", "response": "OK"}
Great. If I use jquery and do a
$.get("http://mylinodesite/django/ping/")
I get
No 'Access-Control-Allow-Origin' header is present on the requested resource.
From what I understand the #csrf_exempt is supposed to get rid of the CSRF header stuff. What gives?

Daniel, turns out you're partially right. It's CORS but it cannot be fixed on the jQuery side. Here's my Django view code that does what I want. Note that it adds the Access-Control-Allow-Origin header to allow requests from all (*) for GET only.
This is also just a demonstration of how to do this all in one file. One could create middleware to do this for all requests if needed, but this works and is a good example of how to do it all in one place so you can see what is going on, and here is the full gist of the entire view file:
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
response = HttpResponse("['ok']", content_type="application/json")
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'GET'
return response

This has nothing whatsoever to do with CSRF, which is for POST actions only and is enforced by Django.
You are doing a cross-domain GET action. Browsers forbid this by default because of what is called the same-origin policy: a JS script can only make requests to the same domain it is loaded from. So, you are being prevented by the browser itself.
To enable requests to named domains, you can use something called CORS, which uses a header in the request. See the jQuery Ajax documentation.

Related

Run django api from postman: CSRF verification failed

I'm trying to run an api using postman. My application is developed in django 1.11.6 using python 3.5.
My app is installed on an ubuntu server. I have no login mechanism to create a csrf token.
These are the steps that I follow:
Click on "import" tab on the upper left side.
Select the Raw Text option and paste my cURL command.
Hit import and I have the command in your Postman builder
Press send button.
My curl command is:
curl -i -H 'Accept: application/json; indent=4' -X POST https://127.0.0.1/users/:register/ -d "id=111&firstname=zinonas&yearofbirth=2007&lastname=Antoniou&othernames="
The error I get is Forbidden (403) - CSRF verification failed. Request aborted.
When I run the curl command via cygwin, it's working properly.
This is the view function that I'm using:
class ApiUserRegister(APIView):
permission_classes = ()
serializer_class = RegisterUserSerializer
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)
# Check format and unique constraint
serializer.is_valid(raise_exception=True)
data = serializer.data
if User.objects.filter(id=data['id']).exists():
user = User.objects.get(id=data['id'])
is_new = "false"
resp_status = status.HTTP_200_OK
else:
user = User.objects.create(id=data['id'],
firstname=data['firstname'],
yearofbirth=data['yearofbirth'],
lastname=data['lastname'],
othernames=data['othernames'])
user.save()
is_new = "true"
resp_status = status.HTTP_201_CREATED
resp = {"user": serializer.get_serialized(user),
"isnew": is_new}
return Response(resp, status=resp_status)
In settings.py I have:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
)
}
Try this.
from django.views.decorators.csrf import csrf_exempt
class ApiUserRegister(APIView):
permission_classes = ()
serializer_class = RegisterUserSerializer
#csrf_exempt
def post(self, request):
serializer = RegisterUserSerializer(data=request.data)
To make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation.
1st option
2nd option
simple just make sure to put as_view()
urlpatterns = [
path('sign_up', views.SignUp.as_view()),
]
update your class to be like this
from braces.views import CsrfExemptMixin
class your_class(CsrfExemptMixin, ......yours_here)
def post(...):
[....]
this will tell django to allow requests without csrf
Django sets csrftoken cookie on login. After logging in, we can see the csrf token from cookies in the Postman. (see image)
CSRFtoken from cookies
We can grab this token and set it in headers manually.
But this token has to be manually changed when it expires. This process becomes tedious to do it on an expiration basis.
Instead, we can use Postman scripting feature to extract the token from the cookie and set it to an environment variable. In Test section of the postman, add these lines.
var xsrfCookie = postman.getResponseCookie("csrftoken"); postman.setEnvironmentVariable('csrftoken', xsrfCookie.value);
This extracts csrf token and sets it to an environment variable called csrftoken in the current environment.
Now in our requests, we can use this variable to set the header.(see image)
Set {{csrftoken}} in your header
When the token expires, we just need to log in again and csrf token gets updated automatically.
Thanks to #chillaranand from hackernoon.com to original post
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.csrf import csrf_protect
#csrf_protect
#csrf_exempt
def home(request):
Add "#csrf_protect, #csrf_exempt" Before the method
In urls file, try this:
urlpatterns = [
url(r'^your_uri/', views.YourView.as_view()),
]
this will tell django to allow requests without csrf

Django REST Framework Swagger - Authentication Error

I followed the instructions in the docs. So here's my view:
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import response, schemas
from rest_framework_swagger.renderers import OpenAPIRenderer, SwaggerUIRenderer
#api_view()
#renderer_classes([OpenAPIRenderer, SwaggerUIRenderer])
def schema_view(request):
generator = schemas.SchemaGenerator(title='Bookings API')
return response.Response(generator.get_schema(request=request))
And I added the following to my urls.py:
url(r'^docs/', views.schema_view),
When I went to the /docs/ page of my project, I got the following error:
401 : {"detail": "Authentication credentials were not provided."} http://127.0.0.1:8000/docs/?format=openapi
In the browser console I got this message:
Unable to Load SwaggerUI init.js (line 57)
When I set the permission_classes of my schema_view to AllowAny, I was able to view my api docs. However, I'm not sure if this is the right way of doing this. Isn't there a way to login as an admin, or any other user to view the docs. Also, how do I provide the auth tokens when viewing this in the browser? Maybe I missed something in the docs.
I think I've found the solution.
In the settings.py, I added the following settings:
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization'
}
},
}
Then when I load the page, I just click on the Authorize button at the upper right and enter this value in the value text field:
Token <valid-token-string>
However, I still needed to set the permission class of the schema view to AllowAny. The auth token just let me switch from different users, allowing me to view different set of endpoints.
Isn't there a way to login as an admin, or any other user to view the docs.
If your only use token authentication, first create tokens for your users, then access the resources by setting the header
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'

Django Rest Framework - how to get Http header information in urls.py

I am making a Django-based API and here is my urls.py.
from django.conf.urls import include, url
api_version = "v1"
urlpatterns = [
url(r'^v1/', include( "api."+ api_version +".urls", namespace=api_version )),
]
What I want to do is to retrieve API version from http accept header. I tried to use django.http.HttpRequest module, which didn't do the trick.
Is there any way to achieve this?
You can't access request on urls.py, see: How Django processes a request
You can configure versioning on django-rest-framework and get version on request object like:
class SampleView(APIView):
def post(self, request, format=None):
if request.version == '1':
return Response(status=status.HTTP_410_GONE)
Or use URLPathVersioning approach.
You cannot access request in url.py, for your particular scnerio i-e for version,
you can use different url for different version of your api.
like
url(r'^v1/', include("api.v1.urls")),
url(r'^v2/', include("api.v2.urls")),
url(r'^v3/', include("api.v3.urls")),

Django returns 403 error when sending a POST request

when I'm using following Python code to send a POST request to my Django website I'm getting 403: Forbidden error.
url = 'http://www.sub.example.com/'
values = { 'var': 'test' }
try:
data = urllib.urlencode(values, doseq=True)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
the_page = response.read()
except:
the_page = sys.exc_info()
raise
When I'm opening any other website it works properly.
example.com is Django website too, and it works properly too.
I think, that's Django config problem, can anyone tell me what should I do to provide access to my script?
Look here https://docs.djangoproject.com/en/dev/ref/csrf/#how-to-use-it.
Try marking your view with #csrf_exempt. That way, Django's CSRF middleware will ignore CSRF protection. You'll also need to use from django.views.decorators.csrf import csrf_exempt. See: https://docs.djangoproject.com/en/dev/ref/csrf/#utilities
Please be advised that by disabling CSRF protection on your view, you are opening a gate for CSRF attacks.
If security is vital to you then consider using #csrf_exempt followed by #requires_csrf_token (see: https://docs.djangoproject.com/en/dev/ref/csrf/#unprotected-view-needs-the-csrf-token). Then, in your script pass this token and that's it.
Does the view that you are posting to have a Django Form on it? If so, I wonder if it's giving a csrf error. I think that manifests itself as a 403. In that case, you'd need to add the {{ csrf_token }} tag. Just a thought.
The response is 403 because django requires a csrf token (included in the post data) in every POST request you make.
There are various ways to do this such as:
Acquiring the token from cookie and the method has been explained in article enter link description here
or
You can access it from DOM using {{ csrf_token }}, available in the template
So now using the second method:
var post_data = {
...
'csrfmiddlewaretoken':"{{ csrf_token }}"
...
}
$.ajax({
url:'url',
type:'POST'
data:post_data,
success:function(data){
console.log(data);
},
error:function(error){
console.log(error);
}
});
Or you can allow the permission to make this post request.
Note: Should be used in the cases where you don't need to authenticate the users for posting anything on our server, say, when a new user registers for the first time.
from rest_framework.permissions import AllowAny
class CreateUser(APIView):
permission_classes = (AllowAny,)
def post(self, request, format=None):
return(Response("hi"))
Further Note that, If you want to make that post request form a different domain (in case when the front of the application is in React or angular and the backend is in Django), make sure the add following in the settings file:
Update the INSTALLED_APPS to use 'coreHeaders' :
INSTALLED_APPS = [
'corsheaders',
]
White list your front end domain by adding following to settings file again:
CORS_ORIGIN_WHITELIST = (
'localhost:8080',
)
Django documentation provides several ways to ensure that CSRF tokens are included. See https://docs.djangoproject.com/en/1.11/ref/csrf/ for details.
I got this error when an authentication Token was expired or when no Token was sent with the request. Using a renewed token fixed the problem.
curl -X POST -H "Authorization: Token mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
or
curl -X POST -H "Authorization: JWT mytoken" -d "name=myname&age=0" 127.0.0.1:8000/myapi/
depending on Token type.
I too had this problem, because I Tried to access the Main endpoint from another endpoint using '../url' URL Jumping.
My Solution was to add another path for the same viewset;
router.register('main/url',ViewSet,'name');
router.register('secondary/url',ViewSet,'name')
But in Your Case You are Trying to access it from a completely different Location, From Django's Point of view So You need to mark you ViewSet with #crsf_exempt middleware which will Disable Security Protocols Related to CRSF.

403 Forbidden error on swfupload and django

I'm trying to use a script for multiple file uploads, like swfupload or uploadify on my django application but no matter what I try, I always get a 403 forbidden error for the upload URL. If I try to run the 'same' code (just different links to same files) independently, it works like a charm.
Any idea if I'm missing something on my main code or is there some kind of setting that I don't know about?
I use uploadify in my django project, get 403 error too, because django has CSRF protection. so i change this function in my views.py solve this problem.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def ajax_flash_upload(request):
This is totally related with CSRF protection. In my case I solved that issue such that,
views.py
def photo_upload(request):
if request.method == 'POST':
for field_name in request.FILES:
....
....
return HttpResponse("ok", mimetype="text/plain")
else:
return render_response(request, 'wpphotos/post/photo_upload.html', {"csrf_token": get_token(request)},context_instance=RequestContext(request))
Because flash useses its own session while uploading, you should set csrf_token value in your middleware such that
swfupload.py
from django.conf import settings
from django.core.urlresolvers import reverse
class SWFUploadMiddleware(object):
def process_request(self, request):
if (request.method == 'POST') and (request.path == reverse('project_name.module_name.views.photo_upload')) and \
request.POST.has_key(settings.SESSION_COOKIE_NAME):
request.COOKIES[settings.SESSION_COOKIE_NAME] = request.POST[settings.SESSION_COOKIE_NAME]
if request.POST.has_key('csrftoken'):
request.COOKIES['csrftoken'] = request.POST['csrftoken']
For the last step, you should set csrftoken as post parameter in your javascript for SWFUpload settings such that
photo_upload.html
window.onload = function() {
swfupload = new SWFUpload({
post_params: {
"csrfmiddlewaretoken": "{{csrf_token}}"
},
upload_url: "/module_name/post/photo_upload/",
flash_url: "/media/flash/swfupload.swf",
file_size_limit : "2.5 MB",
....
....
....
});
};
This is probably related to the flash cookie bug: your client has an authentication cookie that the flash is not including in its request to the server. Since the request doesn't have the auth cookie, it gets rejected with a 403.
Just add an extra data when initializing Uploadify (make your changes on "swf" and "uploader" settings):
$('#file_upload').uploadify({
'formData' : { 'csrfmiddlewaretoken' : '{{csrf_token}}' },
'swf' : '/static/js/uploadify.swf',
'uploader' : '{% url upload %}',
// Put your other options here
});
Thank you very much, brsbilgic. I've tried your solution, and it worked!
By the way, the middleware snippet should be modified to:
if request.POST.has_key('csrfmiddlewaretoken'):
request.COOKIES['csrftoken'] = request.POST['csrfmiddlewaretoken']

Categories