python requests 403 error [duplicate] - python

I have some problem for a while now, I'm experiencing CSRF Cookie not set. Please look at the code below:
views.py:
def deposit(request, account_num):
if request.method == 'POST':
account = get_object_or_404(account_info, acct_number=account_num)
form_ = AccountForm(request.POST or None, instance=account)
form = BalanceForm(request.POST)
info = str(account_info.objects.filter(acct_number=account_num))
inf = info.split()
if form.is_valid():
# cd=form.cleaned_data
now = datetime.datetime.now()
cmodel = form.save()
cmodel.acct_number = account_num
# RepresentsInt(cmodel.acct_number)
cmodel.bal_change = "%0.2f" % float(cmodel.bal_change)
cmodel.total_balance = "%0.2f" % (float(inf[1]) + float(cmodel.bal_change))
account.balance = "%0.2f" % float(cmodel.total_balance)
cmodel.total_balance = "%0.2f" % float(cmodel.total_balance)
# cmodel.bal_change=cmodel.bal_change
cmodel.issued = now.strftime("%m/%d/%y %I:%M:%S %p")
account.recent_change = cmodel.issued
cmodel.save()
account.save()
return HttpResponseRedirect("/history/" + account_num + "/")
else:
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))
Template file:
<form action="/deposit/{{ account_num }}/" method="post">
<table>
<tr>
{{ account_form.bal_change }}
<input type="submit" value="Deposit"/>
</tr>
{% csrf_token %}
</table>
</form>
I'n stuck, I already cleared the cookie, used other browser but still csrf cookie not set.

This can also occur if CSRF_COOKIE_SECURE = True is set and you are accessing the site non-securely or if CSRF_COOKIE_HTTPONLY = True is set as stated here and here

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def your_view(request):
if request.method == "POST":
# do something
return HttpResponse("Your response")

If you're using the HTML5 Fetch API to make POST requests as a logged in user and getting Forbidden (CSRF cookie not set.), it could be because by default fetch does not include session cookies, resulting in Django thinking you're a different user than the one who loaded the page.
You can include the session token by passing the option credentials: 'include' to fetch:
var csrftoken = getCookie('csrftoken');
var headers = new Headers();
headers.append('X-CSRFToken', csrftoken);
fetch('/api/upload', {
method: 'POST',
body: payload,
headers: headers,
credentials: 'include'
})

From This
You can solve it by adding the ensure_csrf_cookie decorator to your view
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def yourView(request):
#...
if this method doesn't work. you will try to comment csrf in middleware. and test again.

If you're using DRF, check if your urlpatterns are correct, maybe you forgot .as_view():
So that how mine code looked like:
urlpatterns += path('resource', ResourceView)
And that's how it should like:
urlpatterns += path('resource', ResourceView.as_view())

I came across a similar situation while working with DRF, the solution was appending .as_view() method to the view in urls.py.

try to check if your have installed in the settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',)
In the template the data are formatted with the csrf_token:
<form>{% csrf_token %}
</form>

This problem arose again recently due to a bug in Python itself.
http://bugs.python.org/issue22931
https://code.djangoproject.com/ticket/24280
Among the versions affected were 2.7.8 and 2.7.9.
The cookie was not read correctly if one of the values contained a [ character.
Updating Python (2.7.10) fixes the problem.

This also occurs when you don't set the form action.
For me, it was showing this error when the code was:
<form class="navbar-form form-inline my-2 my-lg-0" role="search" method="post">
When I corrected my code into this:
<form class="navbar-form form-inline my-2 my-lg-0" action="{% url 'someurl' %}" role="search" method="post">
my error disappeared.

In my case, the problem was that the path to the static files in nginx was incorrectly specified.
sudo tail -F /var/log/nginx/error.log
Check if there are errors in file paths.

I get this error and change this:
<form method="post">
to this:
<form method="POST">
and it's solved !
Just upper case post make the problem !
I have not any issue with this on 127.0.0.1, but when i use 192.168.x.x address this broke my forms.

Problem seems that you are not handling GET requests appropriately or directly posting the data without first getting the form.
When you first access the page, client will send GET request, in that case you should send html with appropriate form.
Later, user fills up the form and sends POST request with form data.
Your view should be:
def deposit(request,account_num):
if request.method == 'POST':
form_=AccountForm(request.POST or None, instance=account)
if form.is_valid():
#handle form data
return HttpResponseRedirect("/history/" + account_num + "/")
else:
#handle when form not valid
else:
#handle when request is GET (or not POST)
form_=AccountForm(instance=account)
return render_to_response('history.html',
{'account_form': form},
context_instance=RequestContext(request))

Check that chrome's cookies are set with default option for websites. Allow local data to be set (recommended).

I was using Django 1.10 before.So I was facing this problem.
Now I downgraded it to Django 1.9 and it is working fine.

I had the same error, in my case adding method_decorator helps:
from django.views.decorators.csrf import csrf_protect
from django.utils.decorators import method_decorator
method_decorator(csrf_protect)
def post(self, request):
...

Make sure your django session backend is configured properly in settings.py. Then try this,
class CustomMiddleware(object):
def process_request(self,request:HttpRequest):
get_token(request)
Add this middleware in settings.py under MIDDLEWARE_CLASSES or MIDDLEWARE depending on the django version
get_token - Returns the CSRF token required for a POST form. The token is an alphanumeric value. A new token is created if one is not already set.

I have just met once, the solution is to empty the cookies.
And may be changed while debugging SECRET_KEY related.

If you are not using {% csrf_token %} tag in the template you are rendering. Django won't set the csrftoken cookie.
To force django to set the csrftoken cookie, add ensure_csrf_cookie decorator in you view.
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def myview(request):

In my particular case, the problem is that I was using the Django rest_framework and forgot to add the following decorators to my function:
from rest_framework.decorators import api_view, renderer_classes
#api_view(('POST',))
#renderer_classes((JSONRenderer,))
def handle_web_request(request):
...

Just want to point out my case here as someone might cross the same fields.
Forbidden (CSRF cookie not set.): /main/staff/products/validation/create
HTTP POST /main/staff/products/validation/create 403 [0.01, 127.0.0.1:55940]
This thing was driving me insane... So, by commenting CSRF middleware
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
)
it gave me
POST Method not allowed.
That was my hint, after all.
I was sure Post method was present.
Turns out my url_patterns was leading to another view by a regex bug.
So no matter what I was doing in my view, #csrf_exempt #ensure_crsf_cookie, looking for .as_view()... I was looking at the wrong view.
So, if nothing works, make sure your are actually being sent to the right view.

You can get this error while deploing Django application with NO SSL.
If this is the case then putting an SSL reverse-proxy or SSL-configured Ingress in front of backend will solve the problem.

In my case, setting CSRF_COOKIE_SECURE to False wasn't enough but setting it to Null/ not specifying the parameter worked.

Method 1:
from django.shortcuts import render_to_response
return render_to_response(
'history.html',
RequestContext(request, {
'account_form': form,
})
Method 2:
from django.shortcuts import render
return render(request, 'history.html', {
'account_form': form,
})
Because render_to_response method may case some problem of response cookies.

Clearing my browser's cache fixed this issue for me. I had been switching between local development environments to do the django-blog-zinnia tutorial after working on another project when it happened. At first, I thought changing the order of INSTALLED_APPS to match the tutorial had caused it, but I set these back and was unable to correct it until clearing the cache.

In your view are you using the csrf decorator??
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def view(request, params):
....

Related

Unit test in Django dont see correct redirect url for successfull login?

In my Django project I create custom admin page (NOT admin from Django). I have 2 login pages. One for admin, second for other users.
Here below you can see urls.py file for admin. I test it and it works fine. After successful login Django redirect user to url which was next parameter (/administration/dashboard/).
I wrote unit test and it raise error. From error I understand that Django redirect to default url (/accounts/profile/). Why unit test dont use settings which I did in urls.py file (next parameter)?
How to fix this problem?
Right now I notice that problem disappear only if I use this code LOGIN_REDIRECT_URL = '/administration/dashboard/' in settings.py. I cant use it cause in the future I will use LOGIN_REDIRECT_URL to my other login page.
I would be grateful for any help!
urls.py:
from django.contrib.auth import views as authentication_views
urlpatterns = [
# Administration Login
url(r'^login/$',
authentication_views.login,
{
'template_name': 'administration/login.html',
'authentication_form': AdministrationAuthenticationForm,
'extra_context': {
'next': reverse_lazy('administration:dashboard'),
},
'redirect_authenticated_user': True
},
name='administration_login'),
]
tests.py:
class AdministrationViewTestCase(TestCase):
def setUp(self):
self.client = Client()
self.credentials = {'username': 'user', 'password': 'password'}
self.user = User.objects.create_user(self.credentials, is_staff=True)
self.data = dict(
self.credentials,
next=reverse("administration:dashboard")
)
def test_administration_authorization(self):
self.assertTrue(self.user)
# logged_in = self.client.login(**self.credentials)
# self.assertTrue(logged_in)
response = self.client.post(
reverse("administration:administration_login"),
self.data,
follow=True
)
# self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse("administration:dashboard"),
status_code=302,
target_status_code=200
)
ERROR:
Traceback (most recent call last):
File "/home/nurzhan/CA/administration/tests.py", line 51, in test_administration_authorization
reverse("administration:dashboard"),
File "/srv/envs/py27/lib/python2.7/site-packages/django/test/testcases.py", line 271, in assertRedirects
% (response.status_code, status_code)
AssertionError: Response didn't redirect as expected: Response code was 200 (expected 302)
forms.py:
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _
class AdministrationAuthenticationForm(AuthenticationForm):
"""
A custom authentication form used in the administration application.
"""
error_messages = {
'invalid_login': (
_("ERROR MESSAGE.")
),
}
required_css_class = 'required'
def confirm_login_allowed(self, user):
if not user.is_active or not user.is_staff:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={
'username': self.username_field.verbose_name
}
)
login.html:
<form action="{% url 'administration:administration_login' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
<input type="hidden" name="next" value="{{ next }}"/>
</form>
Have you tried removing the follow=True in the POST request. You are checking for a redirection but you are telling the requests modules to follow the redirection, so your response will be directly the page and not the 302 redirection HTTP response.
To be more explicit. You are sending a request with requests.post(follow=True), that will follow the 302 redirection to the destination page and your response will be a HTTP 200 with the destination page. Even if the destination page is the one you want, the test assertion will fail because is looking for a HTTP 302 code in your response and you've already followed the redirection.
Django isn't checking next in extra_context but in GET and POST params.
extra_context is used to update your template context. So if you want to pass the values for variables to your template you can set those with extra_context.
However, you can fix your test by either setting LOGIN_REDIRECT_URL in settings.py or passing next as a POST or GET param.
params = dict(**self.credentials, next=reverse("home:index"))
response = self.client.post(
url,
params,
follow=True
)
NOTE:
self.credentials should be unpacked in the dict being applied to self.data.
self.data = dict(
**self.credentials,
next=reverse("administration:dashboard")
)
If you wanted to test without making this change, I can recommend another way to test this.
You need to render your template in a browser and fill out the login form. This way your browser client will make the POST request with next passed as a parameter.
You can get this done with a headless browser using selenium webdriver.

Python/Django - How to get the page URL before navigating to a new view

I have a link that links to a url, like so:
Sign in
This links to a new view in Django, my URL pattern looks like this:
url(r'^sign-in/', sign_in, name="sign_in")
In my sign_in view, I would like to have access to the URL where the user originally clicked the link. Specifically, I would like to add this URL has a parameter called returnUrl to a new URL. My sign_in view can then redirect to this new URL, and the corresponding view will have access to the returnUrl parameter.
For example:
The user clicks 'Sign in'
The website navigates to example.com/sign-in/
The sign_in view redirects to sign_in_page adding the URL from step 1 as a parameter.
sign_in_page has access to the URL parameter.
How can I achieve this in Django? My views look like this...
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.core.urlresolvers import resolve
def sign_in(request):
# I need the original URL the user came from here
# to add it as `returnUrl` below:
return HttpResponseRedirect(reverse('sign_in_page') + '?returnUrl=example.com/whatever')
def sign_in_page(request):
return render(request, 'sign-in.html')
Please let me know if I'm going down a wrong and dark path by using a redirect. To me it seemed like the only way to add a URL parameter - which is important for my implementation. If you know of a way to do it without the redirect - let me know!
If you want to pass data between views, you may use either POST data or GET. In your case GET data is what you want.
So you should use the following URL.
"{% url 'sign_in' %}?returnUrl={{request.path}}"
Then after in your view:
check if GET variable 'returnUrl' was sent
redirect to the next URL
else redirect to the default page
In the following example I am using Class based View inherited from FormView
def get_success_url(self):
if self.request.GET.get('next', ''):
return (self.request.GET.get('next', ''))
return reverse_lazy('experiment_list')
Note: make sure you include the following in your settings, to make sure you get the request.path variable in each template:
## example: settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
# other items...
'django.core.context_processors.request',
# other items...
)
If you want to redirect to referer, Then you can try like this
from django.http import HttpResponseRedirect
def sign_in(request):
# Do your logic here
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
referer = self.request.META.get('HTTP_REFERER')
try this in the view where you need to get the previous url, hope this is what you are looking for

Redirecting Django built-in login View to a URL taking user's pk as argument

I am trying to redirect to a URL taking user's pk as argument after successful log-in using Django's built-in login view.
Instead of dynamic {{ next }} variable in my login.html I have a generic landing view of logged-in users;
<input type="submit" value="login" />
<input type="hidden" name="next" value="{% url 'userredirect' %}" />
In my urls.py I have;
url(r'^users/', views.users, name='userredirect'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
and in my views.py I have
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk':request.user.id})
return HttpResponseRedirect(url)
What I am doing here is redirect to a detail view that I have named UserHome on the user model after successful login using 2 redirects as I do not know of a way to redirect to UserHome directly (it takes user's pk as argument). It works and I indeed get redirected to the user's homepage when checking via the browser.
Reference;
The "next" parameter, redirect, django.contrib.auth.login
But when running the below test
def test_page_redirects_to_user_home_on_login(self):
"""
Test to assure that the login page redirects to the user's
home page
"""
username = "someusername"
password = "somepassword"
user = User.objects.create_user(username=username,
password=password)
user.save()
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
assert response.path == self.client.get(reverse("userhome",
kwargs={"pk":user.id}
)
)
I get the below failure
AttributeError: 'HttpResponseNotFound' object has no attribute 'path'
It seems the test client gets no page. Would it be that I am using the userredirect view simply for redirecting and the client do not go ahead and get the UserHome class view to its context.
I'm a newbie to Django/Python. Someone please sort this out for me :).
I look forward either to a way where I can redirect directly from the template for login view to UserHome or a way to rewrite my test.
Hard to say without much more insight in your project. Here are a few possibilities and such.
Response has no path
response indeed has no path, you probably wanted this:
assert response.wsgi_request.path == reverse("userhome", kwargs={"pk":user.id})
Include next in your test
You're simulating data from the login form, but you're omitting the next field.
Add it to the POSTed data:
{"username":username,
"password":password,
"next": '/users/',}
Take a look what's in the response
It might help to see what's in the response in your test. For example:
print(response.redirect_chain)
Perhaps you're not even reaching the login page?
Are you missing LOGIN_URL in your settings.py?
LOGIN_URL = '/login/'
Without it, you'll be redirected to '/accounts/login/', which might be the 404 you're seeing.
Finaly - why? :)
Perhaps you have some special use case, but I'd usually read user's id (a.k.a. pk) from request.user. That way I (for example) can't access example.com/<your_id> and access your homepage. Of course, that might be just what you intend. In that case I'd still have a separate URL for current user, it will probably pay off later. Something like this:
...
url(r'^/', UserHome.as_view(), name='userhome'),
url(r'^(?P<pk>\d+)/', UserHome.as_view(), name='userhome'),
...)
class UserHome(DetailView): # also protect with some LoginRequiredMixin
model = User
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
id = self.kwargs.get('pk', self.request.user.id)
return queryset.filter(id=id).get()
First things first: The error you get is because the line
response = self.client.post(reverse("userlogin"),
{"username":username,
"password":password},
follow=True)
raises a 404 error, hence resonse is a HttpResponseNotFound.
Before testing anything else is it a good practice to first test that your request was successful. Something along the line of:
self.assertEqual(response.status_code, 200)
Also, you are hard-coding url's which goes against DRY and is often the source for trouble (maybe it is the case here).
It would be better to name all your urls:
url(r'^users/', views.users, name='user_redirect'),
and then use this in your template
<input type="hidden" name="next" value="{% url 'user_redirect' %}" />
and this in your view
from django.core.urlresolvers import reverse
#login_required
def users(request):
url = reverse('userhome', kwargs={'pk': request.user.id})
return HttpResponseRedirect(url)
And finally, you are taking an unnecessary step with the redirect. Assuming UserHome is a DetailView on User, you could have this code:
##urls.py
url(r'^users/', UserHome.as_view(), name='userhome')
##views.py
from django.views.generic import DetailView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
class UserHome(DetailView):
model = User
def get_object(self, queryset=None):
return self.request.user
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(UserHome, self).disatch(*args, **kwargs)
This would also ensure that no user accesses another user's "userhome".
Doing all this should help you find what went wrong with your code. Good luck!

Django: href {% url %} issue

Why does
View answers
in my template translate to this interpretation by Django:
Request URL: http://127.0.0.1:8000/questions/%7B%%20url%20'answers.views.display_answers'%20Question.id
which of course leads to an url mismatch error.
Seems like its reading in my '{' in ASCII form. Can anyone enlighten me as to why it is so?
EDIT:
This was how i rendered the template--
return render(request, 'display_questions.html', context)
and the template contains the href. My display answer view redirects to another view as such:
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return redirect('view_answers.html', context)
Error:
The current URL, questions/{% url 'answers.views.display_answers' Question.id, didn't match any of these.
This is correct. If not - your urls.py seems to be wrong. Please post it.
View answers
Edit
Here's a better version of your view.
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.shortcuts import render_to_response, redirect, get_object_or_404
def display_answers(request, q_id):
q = get_object_or_404(Question, id=q_id)
ans_list = Answer.objects.filter(question=q)
context = {'question': q, 'ans_list': ans_list}
return render_to_response('view_answers.html', context, RequestContext(request))
The problem is your use of redirect in the view. You should be using render or render_to_response unless you actually want to redirect the browser. (Observe using Fiddler, Firebug, or Chrome's developer tools and you'll see that it is redirecting.)
The reason this is not as obvious of a problem is because redirect may take a URL as its first argument. 'view_answers.html' is being interpreted as a relative URL, which may or may not map to a URL in your URLconf. If it does map to a URL, then you get a false positive result that everything appears to be working, but if your web server handles that link instead of Django, then it may just send the template page back in plain text. The solution is, as I said, either render or render_to_response for rendering a page, or redirect with the name of a view or the special name of a URL pattern to redirect to a different view.
Modify your redirect in your display_answers view to use the name of the view, instead of the name of your template (view_answers.html) and don't pass it the context (redirect doesn't take the context as a parameter):
return redirect('your_view_answers_view')

Why is django.test.client.Client not keeping me logged in

I'm using django.test.client.Client to test whether some text shows up when a user is logged in. However, I the Client object doesn't seem to be keeping me logged in.
This test passes if done manually with Firefox but not when done with the Client object.
class Test(TestCase):
def test_view(self):
user.set_password(password)
user.save()
client = self.client
# I thought a more manual way would work, but no luck
# client.post('/login', {'username':user.username, 'password':password})
login_successful = client.login(username=user.username, password=password)
# this assert passes
self.assertTrue(login_successful)
response = client.get("/path", follow=True)
#whether follow=True or not doesn't seem to work
self.assertContains(response, "needle" )
When I print response it returns the login form that is hidden by:
{% if not request.user.is_authenticated %}
... form ...
{% endif %}
This is confirmed when I run ipython manage.py shell.
The problem seems to be that the Client object is not keeping the session authenticated.
Just happened to me when retesting an app that has been working and forgotten for some months.
The solution (apart from updating to Django 1.2) is Patch #11821. In short, Python 2.6.5 has some bugfix in the Cookie module, triggering an edge case bug in the test client.
FWIW, an update to Django 1.2 (I was running 1.1.1 before) fixed it. I have no idea what was broken there, considering when I last ran that test suite about 2 weeks ago, it worked great.
I use RequestContext to get the logged in user into the template context.
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required
from django.template import RequestContext
#login_required
def index(request):
return render_to_response('page.html',
{},
context_instance=RequestContext(request))
and in the template
{% if user.is_authenticated %} ... {{ user.username }} .. {% endif %}
This works as expected (I don't get to this page without logging in, and when I get there, the username is present in response.content) when driven through the test client.

Categories

Resources