403 Error trying to do a post request in django - python

I keep getting 403 errors when I try to do a post request in Django. I disabled django.middleware.csrf.CsrfViewMiddleware and i still have this line
url(r"viewing_room_capture", csrf_exempt(EmailCapture.as_view()), name="email_capture_endpoint",)
Why am I getting a 403? It doesn't even look like my view executes, the request short-circuits before the view runs.
And yes I do want to disable the csrf protection on my forms. I seem to remember this working a week ago but I've since rebuilt my local environment from scratch (docker-compose app) and it no longer works. I had 5 working API endpoints before this and now none of them work, I haven't touched this app in 1.5 weeks before coming back to it today and finding everything broken. In fact, I have a development deployment using the same application code running right now in the cloud and I'm not getting 403 errors on my API endpoints (i checked the commit history already, nothing that could have caused this was added since my last deploy). My middleware is:
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"app.site_translation.middleware.TranslationMiddleware",
"django.middleware.common.CommonMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.security.SecurityMiddleware",
"app.base.middleware.AdditionalSecurityHeadersMiddleware",
"app.base.middleware.CookieMiddleware",
"app.base.middleware.DomainRedirectMiddleware",
"app.base.middleware.CustomRedirectMiddleware",
"app.base.middleware.LegacyURLsMiddleware",
# Can be used instead if wagtail#4491 is merged:
# 'wagtail.contrib.redirects.middleware.RedirectMiddleware',
"debug_toolbar.middleware.DebugToolbarMiddleware",
]

Figured it out, it had to do with signing into the application. I can't do a post request without csrf if i'm logged in already.

Related

CORS not working in Django but settings seem correct

I am trying to make a POST call to Django from a React Native Web front end on different subdomains.
I thought I had configured CORS correctly, but that does not seem to be the case.
Here's what my Django settings.py looks like:
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = ['*']
CORS_ALLOWED_ORIGINS = ['https://api.example.com', 'https://example.com', 'https://www.example.com' ]
CSRF_TRUSTED_ORIGINS = [
'https://api.example.com', 'https://example.com', 'https://www.example.com'
]
ALLOWED_HOSTS = ["0.0.0.0", "api.example.com", "example.com"]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
]
INSTALLED_APPS = [
...
'corsheaders',
...
]
What exactly am I doing wrong here? The error I'm getting is this:
Access to XMLHttpRequest at 'https://api.example.com/api/v1/pagescreate/' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
And this is my Django view:
class PageCreateView(generics.CreateAPIView):
queryset = Page.objects.all()
serializer_class = PageSerializer
What could be causing this? Am I missing some setting in React? I'm using axios to make the calls, with the only header being "Content-Type": "application/json"
EDIT: Could this be due to some nginx rule on my server? Or maybe my Kubernetes configuration? I am using Docker to set it up the container and can easily link the Dockerfile, or any information from my Kubernetes setup
I had this issue before and I suggest to use:
CORS_ORIGIN_ALLOW_ALL = True
that will allow all origins
with this config ofcourse:
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
INSTALLED_APPS = [
...
'corsheaders',
...
]
I think, this could be resulted from the sequence of middlewares in the settings. (I am not completely confident though)
Can you try the following sequence of middleware
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
and just remove
CORS_ALLOW_HEADERS = ['*']
from the settings.
Let me know if this works out
CORS_ALLOW_CREDENTIALS = True makes CORS_ALLOW_HEADERS = ['*'] be translated literally i.e. not as wildcard.
If you want to add custom headers you need to extend the default list as it's done in the doc;
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = list(default_headers) + [
'my-custom-header',
]
As the request has Content-Type of application/json this is not a simple request as per MDN. So this request triggers CORS preflight request which includes Access-Control-Request-Headers request header. This request header elicits Access-Control-Allow-Headers response header.
Value of this response header can be * to indicate wildcard value in the absence of credentials. But the * is interpreted literally when HTTP cookies or authentication information are present in the request as it's done here with CORS_ALLOW_CREDENTIALS = True.
Here in CORS preflight Access-Control-Request-Headers has value of Content-Type but Access-Control-Allow-Headers is a literal * because of CORS_ALLOW_HEADERS = ['*']. So the request is not authorized, that's why the error No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET works here because it is a "simple request" and doesn't trigger CORS perflight.
CORS
Access-Control-Allow-Headers
I have had a boatload of CORS issues with Django. Generally, you might try to use:
CORS_ALLOWED_ORIGINS = ['*']
CSRF_TRUSTED_ORIGINS = ['*']
(Note: This is just boilerplate and you probably don't want to do it in production; hunting down the actual issue is a necessity in the end)
to make sure it's in your Django setup. However, as I have mentioned here: What can cause Chrome to give an net::ERR_FAILED on cached content against a server on localhost?
There are all sorts of things that can cause CORS errors (sometimes not even registering them as CORS errors) and it can show up coming from all sorts of locations. The real issue is best hunted by digging through your logs.
(Note: this answer shows how to put your Django logs into files, which you probably should be doing. The standard Django REST-response startup logs will contain useful information)
Step one is to see if a specific request is hitting your Django logs at all. If it is, your CORS settings within Django are the problem. You can easily tell why it's getting rejected because Django will have the fully qualified (MYSUBDOMAIN.example.com) domain that it has rejected in the log. This makes changing your Django settings a snap; simply add the rejected domain to your allowed origins.
Note: If you're not a BASH / Django pro, going to the log directory (should be referenced in your settings.py) where Django is stuffing its logs and executing ls -alrt will give you the last modified log.
I have always debugged this problem in Apach2 on Ubuntu, though. So I had a log at /var/log/apache2 that showed me exactly which domain was being rejected and why. (When Apache rejected the request; if you're putting a web server in front of Django it will also probably have CORS policies and plugins) If you're using ngnix on docker, the following question (Where are logs of docker nginx conainter stored in host) suggests you use this command line command to get the log files out of docker and see what they're doing.
docker inspect --format='{{.LogPath}}' <nginx-containter-id>
I personally will often end up booting a Docker instance from a command line and going to the BASH prompt to debug it. Advice on that is outside the scope of this question.
Going out a step further, it is possible that this CORS policy problem is even farther out on your network stack. There are times when misconfiguration of security policies, bad redirects, or other infrastructure-related issues are misunderstood by Chrome as CORS issues. A CORS interpretation can even be a browser configuration issue, in some cases. I, on occasion, have booted up a virtual machine to set my security levels in Chrome to the absolute minimum and try it, just to see if CORS was really the issue.
Sometimes it wasn't. Sometimes it was that something else - a load balancer, a firewall, a misconfigured AWS security group - was the culprit.
So, yeah, digging this out can be a full on bug safari rather than simple hunt. But the core thing to check first is whether your Django logs are spitting out anything after each REST request. If they're not you need to go up the chain because you're not talking to Django at all.
Ah, another note: We get CORS errors when Django can't write to its log files.
I had a similar issue which was solved by shifting corsheaders.middleware.CorsMiddleware above django.middleware.common.CommonMiddleware,in MIDDLEWARE array (settings.py).
Disclaimer: not a django dev.
But you are using a non-whitelisted value for Content-Type which means your POST request will be "preflighted". Can you confirm the OPTIONS request is properly handled by your API?
If you're running this in K8s, see if including the following to ALLOWED_HOSTS helps:
gethostname(), gethostbyname(gethostname())
Can you try:
CORS_ORIGIN_WHITELIST = (
'https://example.com' )
I did something very similar to what you're doing and this configuration worked for me:
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'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',
]
CORS_ORIGIN_WHITELIST = [
'https://website.com'
]
I think its possible that you are missing a couple middlewares:
django.middleware.csrf.CsrfViewMiddleware
corsheaders.middleware.CorsPostCsrfMiddleware
otherwise I believe there is no problem with your CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS settings
Are you sure that CORS_ALLOW_HEADERS allows you to set '*'?
Based on here, it looks like it is designed to be extended rather than replaced:
https://github.com/adamchainz/django-cors-headers/blob/2d22268ff59aa0b79820ea5b8983efd1e514ec4c/README.rst#cors_allow_headers
This test also led me to think that it is required to be extended rather than replaced
https://github.com/adamchainz/django-cors-headers/blob/5067faefeb22beffcf1b12dd7ad9d3fb6d4b26e4/src/corsheaders/checks.py#L20-L21
I would suggest trying that with the default or try extending it from the above link
I believe that is a library-specific value and not a browser or request CORS value
edit: added test link to github.com
As explained here, The CORS Access-Control-Allow-Origin line expects in one of these two formats:
Allow everything: probably not what you want
Access-Control-Allow-Origin: *
Allow a single specific origin:
Access-Control-Allow-Origin: [SCHEME]://[HOST]:[PORT_OPTIONAL]
Scheme: AKA protocol, this can be HTTP, HTTPS .
Host: This has to exactly match the requesting origin, so subdomain.domain.com or domain.com
Port: [Optional, but important for you] Leaving the port out is the same as putting default port :80 if your scheme is HTTP and :443 if your scheme is HTTPS.
Also, The server https://example.com which you are making the request to has to implement CORS to grant Script from your website access. Your Script can't grant itself permission to access another website.
I used a CORS extension named allow-cors-access-control from chrome web store.
Or, You can use a change-origin chrome plugin like this:
Moesif Orign & CORS Changer
You can make your local dev server (ex: localhost:8080) to appear to be coming from 172.16.1.157:8002 or any other domain.
Make sure to restart the server after adding the above configuration and clear the cache and hard reload. It worked for me.

How to prevent URLconf settings from being carried onto new project?

I recently worked on a project that had an app named 'catalog'. While I was working on it, I changed the URLconf so that the root URL could be redirected to the app.
I wrote the below code to do this:
# within the project's URLconf file
from django.views.generic import RedirectView
urlpatterns += [
url(r'^$', RedirectView.as_view(url='/catalog/', permanent=True)),
]
I was working on the local development server, and the root URL (127.0.0.1:8000) was successfully redirected to the 'catalog' app (127.0.0.1:8000/catalog/).
However, when I created a new project, the root URL of this NEW project ALSO tried to redirect to the 'catalog' app of the previous project.
So where as I should be seeing the "it worked!" page at the root URL for the new project, I am instead redirected to the 'catalog' app's URL of the previous project, where the 404 page is displayed (obviously, because the 'catalog' app is not part of the new project).
Instead of this:
It Worked Page
Seeing this:
404 Page
It seems to me that the settings from the previous project have somehow affected the local server permanently so that the modified URLconf setting is carried on to any subsequent projects.
I could not find exactly what was causing this issue so I just ran the new project on a different port (8001) using the
python manage.py runserver 8001
command, and this seemed to fix the issue. However, I regard this only as a temporary workaround and I want to find out the root cause of the issue.
If I can't fix it, I would like to "reset" the default port (8000) so everything goes back to default settings.
Is there a way to completely "reset" either the local server or django itself so that all the settings go back to how they were released?

No csrf token for Django web page

I have a new Django app with REST endpoints and static html page that triggers XMLHttpRequest REST queries. POSTs fail with "CSRF cookie not set.".
If I open a client debugger, document.cooke returns "". AFAIK, this should have "csrftoken".
In settings.py, MIDDLEWARE_CLASSES contains 'django.middleware.csrf.CsrfViewMiddleware' by default. Any idea what might be wrong?
Add ensure_csrf_cookie decorator to your view.
https://docs.djangoproject.com/en/1.9/ref/csrf/#django.views.decorators.csrf.ensure_csrf_cookie
If that doesn't help you might want to read these docs
https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax

Django rest basic authentication issue (HTTP 400)

I am building an API which has basic authentication enabled. When I'm trying to add an instance to the database (on a viewset which has the AllowAny setting) using the browsable api it. This works when I am not logged. However when I'm not logged in it gives me an error:
HTTP 400 Bad Request
Content-Type: application/json
Vary: Accept
Allow: POST, OPTIONS
{
"product": [
"This field is required."
],
"buyername": [
"This field is required."
],
"buyeremail": [
"This field is required."
]
}
This is the viewset:
class TicketBuyerViewSet(mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = Ticket.objects.all()
serializer_class = TicketSerializer
permission_classes = (IsOwnerOrReadOnly,permissions.AllowAny)
def perform_create(self, serializer):
serializer.save(isPayed=False,checkedIn=False,isRefunded=False,checkedInDate=None,paymentID="",paymentDate=None)
And this is my urls.py file:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^', include('events.urls')),
url(r'^docs/', include('rest_framework_swagger.urls')),
]
urlpatterns += [
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
]
I have been having this problem only recently. I made the snippets app from the django docs which worked perfectly for about a week. However, since a couple day I have the same problem with this api as with my 'own' api. I have tried chrome and firefox as well.
Edit: While I get why the http error codes may be confusing opposed to my question I do highly suspect the error lies in the django-rest authentication because when I log out and I fill in the EXACT same data it DOES work. Here are the response error codes for the PUT request when I'm logged in and logged out respectively:
[03/Nov/2015 20:38:44] "POST /ticketsbuyer/ HTTP/1.1" 400 10513
[03/Nov/2015 20:39:24] "POST /ticketsbuyer/ HTTP/1.1" 201 4543
Edit 2: I downloaded the exact source code from the django-rest-framework-tutorial github. I created a superuser and the EXACT same thing happened, for some reason django rest browsable api or my browser is not sending the post data correctly.
Edit 3 For some reason it worked for me to downgrade to version 3.2.5 of the rest-framework. I'm not the only one with this problem: Django Rest Framework - Browsable API Form always returns 400 bad request
There's an opened bug about this: https://github.com/tomchristie/django-rest-framework/issues/3588
However, I wasn't able to reproduce yet. Help is welcomed to understand what's going on.
Edit: thanks, it seems indeed that the authentication is the key in this bug.
Edit: upgrading to 3.3.1 should fix the issue.
HTTP400 is not generally an authentication error. Typically it is a data error. The message it is sending back says that you did not send the required fields so check the data you are sending and make sure it is meeting all the required fields. If it was an authentication issue it would be returning a HTTP401

Facebook authentication error in Heroku based django website

I have fully created a Django website with python-social-auth for facebook authentication and have hosted it on heroku(free version). The problem however is,that my Facebook authentication,which was working perfectly locally,is not working and it throws an error as follows:-
Given URL is not allowed by the Application configuration: One or more of the given URLs is not allowed by the App's settings. It must match the Website URL or Canvas URL, or the domain must be a subdomain of one of the App's domains.
I have configured my facebook settings , wherein the site url is the one hosted on heroku (https://quiet-hamlet-3248.herokuapp.com/). Also,I haven't changed my API keys/secret.
Any help would be appreciated.
So I just figured it out.
As mentioned here , one needs to update his settings file to include the line SOCIAL_AUTH_REDIRECT_IS_HTTPS = True
This is because heroku fails to pass the headers required to identify the app.

Categories