Django Book OUTDATE CSRF protection - django

Hello I'm a newbie trying to use django to register some users, I have been reading the Django Book and am on a chapter about registration,http://www.djangobook.com/en/2.0/chapter14/ when I do the instructions I get this
Forbidden (403)
CSRF verification failed. Request aborted.
Help
Reason given for failure:
CSRF token missing or incorrect.
In general, this can occur when there is a genuine Cross Site Request Forgery, or when Django's CSRF mechanism has not been used correctly. For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function uses RequestContext for the template, instead of Context.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.
You're seeing the help section of this page because you have DEBUG = True in your Django settings file. Change that to False, and only the initial error message will be displayed.
You can customize this page using the CSRF_FAILURE_VIEW setting.
I put the {% csrf_token %} template tag inside the post tag and it still gives me this error. thanks
# views.py
#
# Copyright 2012 Talisman <KlanestroTalisman#gmail.com>
from django.shortcuts import render_to_response
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.http import HttpResponseRedirect
def home (request):
return render_to_response('FirstTemplate.html',)
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/books/")
else:
form = UserCreationForm()
return render_to_response("register.html", {
'form': form,
})
forms
{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<h1>Create an account</h1>
<form action="" method="post"{% csrf_token %}>
{{ form.as_p }}
<input type="submit" value="Create the account">
</form>
{% endblock %}

Djangobook uses a pretty old version of django, you may be on a newer version, I have tried the information and csrf section is definitely outdated since they had some modification to the way this is handled in newer versions, match your django version with the book version, also some frequent reasons for this error (In addition to the middleware thing mentioned by pahko) are
not using csrf_token tag in template
not using RequestContext class - replace Context with RequestContext
like this
from django.template import RequestContext
and in the render statement
return render_to_response("home/index.html", c, context_instance=RequestContext(request))
note: use your own template path in above statement.

try to put this in your middleware config in settings.py
MIDDLEWARE_CLASSES = (
'django.middleware.csrf.CsrfViewMiddleware',
)
hope it helps

Related

CSRF token missing, causing verification failure in Django app's login form (accessed over a forward proxy)

I maintain a simple Django web application where users congregate and leave each other text + photo messages. The server employs SSL. I also use a CDN to serve static assets, in case that matters.
My problem is that upon trying to log in an existing user, I get a CSRF Verification failed error. Peculiarly, many users report getting this error the first time they try logging in, but that it works the second time around right after. Also, a proxy is involved in between - all the reporters of this error are on the said proxy. Without the proxy, it works perfectly. Looking at Resources in Developer Tools for Chrome, I've found that the CSRF token is correctly present when the website is accessed without the proxy in the middle, but is entirely missing when on the proxy.
What special measures am I to take on this?
I use a vanilla logging in process, i.e. a contrib function. In my urls.py, I have the url pattern:
url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}, name="login"),
In settings.py, I have:
MIDDLEWARE_CLASSES = (
# 'debug_toolbar.middleware.DebugToolbarMiddleware',
'myproject.middleware.XForwardedFor.XForwardedForMiddleware',
'user_sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
#'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'myproject.middleware.HellBanned.HellBannedMiddleware',
#'request.middleware.RequestMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'mobileesp.middleware.MobileDetectionMiddleware',
# Uncomment the next line for simple clickjacking protection:
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
Whereas the login function in views.py found in django.contrib.auth is:
#sensitive_post_parameters()
#csrf_protect
#never_cache
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
current_app=None, extra_context=None):
"""
Displays the login form and handles the login action.
"""
redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
# Okay, security check complete. Log the user in.
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponseRedirect(redirect_to)
else:
form = authentication_form(request)
request.session.set_test_cookie()
current_site = get_current_site(request)
context = {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
Is the above function missing anything? BTW, my login template is quite simply:
{% extends "base.html" %}
{% block content %}
<div class="margin">
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<p>{{ 'Nickname:' }}
{{ form.username }}</p>
<p> {{ 'Password:' }}
{{ form.password }}</p>
<input class="button" type="submit" value="OK"><br>
</form>
<br>
</div>
{% endblock %}
Ideally, I don't want to use csrf exempt, since I feel that's a workaround, and doesn't really untangle this tangle.
Can anyone please advise what I need to do? I've seen similar questions asked on SO before, but they don't work for me because:
here: this OP had written an erroneous method in views.py, whereas I'm simply relying on the vanilla django.contrib.auth.views.login method. As the docs state: If you’re using the render() function, generic views, or contrib apps, you are covered already since these all use RequestContext.
here: Same problem as above it seems.
here: this OP was missing {% csrf_token %} in her Django template, I'm not.
Thanks in advance. I'm a newbie in this domain, so apologies if the solution is obvious and it's eluding me. Btw, in case it matters, I employ legacy Django (v 1.5.1) for this particular project.

WTForms error handling with seamless user experience

I'm new to WTForms, and from what I've learned so far, I find the validation error handling a bit tricky.
First off, I can't implement the native HTML <input required >;
Secondly, when the form failed validation, I have to rerender the page template where the form is at, and if my form is placed on bottom of a page, the user will see the page 'refreshed' and have no idea what is going on.
Does anyone have suggestions on how to integrate WTForms more seamlessly? Any comments, resources, urls, code samples are welcomed, thanks!
Here are some of my related codes:
# /forms.py
from wtforms import Form, BooleanField, StringField, PasswordField, validators
class RegistrationForm(Form):
email = StringField('Email Address', [
validators.required(),
validators.Email(message='Please enter a valid email address'),
validators.length(min=6, max=35)
])
password = PasswordField('New Password', [
validators.required(),
validators.DataRequired(),
validators.length(min=6, max=35)
])
# /views.py
from flask import Flask, Blueprint, flash, render_template, redirect, request, url_for
from forms import RegistrationForm, LoginForm
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
form = RegistrationForm()
context = {
"form": form
}
if request.method == 'POST' and form.validate():
submitted_form = request.form
return redirect('/welcome')
else:
return render_template('form.html', context = context)
# /form.html
<form class="form" action="" method="POST" name="register">
{% for field_name, field_errors in context.reg_form.errors|dictsort if field_errors %}
{% for err in field_errors %}
<li class="error">{{ context.reg_form[field_name].label }}: {{ err }}</li>
{% endfor %}
{% endfor %}
<ul class="form-fields center">
<li class="form-field">
{{ context.form.email(class='email', placeholder='email') }}
</li>
<li class="form-field">
{{ context.form.password(class='password', placeholder='password') }}
</li>
</ul>
</form>
As far as I understand, WTForm will always have to refresh the page in order to show the errors. I worked this out is by letting the front-end to validate the form for me. Do not know if it possible on your case, but I've used AngularJS to do the trick. It's quite easy, not a single line of code is required if you need simple validation like email format, password length and etc, it's all done by html attributes. You can even disable the submit button until your form is ready to go.
Check out this CodePen
You can scroll to the bottom to the page using this, if still needed:
var objDiv = document.getElementById("your_div");
objDiv.scrollTop = objDiv.scrollHeight
If you need a more robust solution, you might come up with a API that return true or false to whatever field you want to validate. Then make angular make a http request to check it as you type, this way you are 100% sure you form is going to validate. But depending on what you are checking, you might expose a security hole. There is an excellent blog post about this on Ng-Newsletter
For a better experience with forms in Flask you should use the Flask-WTF extension. Install it:
$ pip install flask-wtf
import it
from flask.ext.wtf import Form
and use it along with wtforms module.
.py file:
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
.html file:
<form method="POST">
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }} #this is your submit button
</form>

django allauth login & signup form on homepage

I have been looking for a solution for some time now and I am not able to wrap my head around this. All I am trying to accomplish is to have the allauth login and signup form on the same page and on the homepage instead of under the /accounts urls. Does anyone have experience with that or has a solution they could share or point me in the right direction? Any help would be appreciated.
First we take create a custom view using allauth's signup view
from allauth.accounts.views import SignupView
from allauth.accounts.forms import LoginForm
class CustomSignupView(SignupView):
# here we add some context to the already existing context
def get_context_data(self, **kwargs):
# we get context data from original view
context = super(CustomSignupView,
self).get_context_data(**kwargs)
context['login_form'] = LoginForm() # add form to context
return context
Validation errors will not be rendered here for login form, we then need to create a custom LoginView, but for now let's move on to the template
<button id="toggleForms">Toggle Forms</button>
<form method='post' action='{% url 'yourviewurl %}' id='signup'>
{% csrf_token %}
{{ form.as_p }}
<input type='submit' value='Sign Up'>
</form>
<form method='post' action='{% url 'loginurl' %}' id='login' hidden="hidden">
{% csrf_token %}
{{ login_form.as_p }}
<input type='submit' value='Log In'>
</form>
Add some javascript to toggle these.
The actions point the forms in different directions. Normally we would use formsets for this but since All-auth's signup form is not a Form object this may be the quickest way to do it.
These all go in views.py of any app you choose, the tags go inside of a template defined in settings.py, TEMPLATE_DIRS or Dirs list in django1.8
these steps worked for me
1. goto allauth\account\views
In the loginView class under get_context_data function add the the following code signup_form context rendering to the ret
ret.update({"signup_url": signup_url,
"site": site,
"redirect_field_name": self.redirect_field_name,
"redirect_field_value": redirect_field_value,
-->"signup_form":get_form_class(app_settings.FORMS, 'signup',SignupForm)<--
})return ret
in your app views.py
def homepage(request):
template = 'account/login.html'
context ={}
return render(request, template, context)
your urls.py
from .views import homepage
path('', homepage, name='home'),
in your account/login.html
{% include "account/signup.html" with form=signup_form %}
see https://github.com/believeohiozua/django-allauth/blob/master/allauth/account/views.py for sample code
if you are okay with the above repository you can just install with
pip install git+https://github.com/believeohiozua/django-allauth.git

django redirect after POST not working

I have apparently forgotten something really basic about django. Here's my views.py:
def sourcedoc_create(request):
if request.method == 'POST': # If the form has been submitted...
form = SourcedocForm(request.POST, request.FILES) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
handle_uploaded_file(request.FILES['doc'])
form.save()
return HttpResponseRedirect('/index/') # Redirect after POST
else:
form = SourcedocForm() # An unbound form
return render_to_response(
'sourcedoc_create.html',
{'form': form},
RequestContext(request)
Here's the relevant part of urls.py:
url(r'^$', index),
url(r'^index/', index),
url(r'^sourcedoc/create/', sourcedoc_create),
When I run the app, I create the record in the database, and the uploaded file appears successfully in the relevant directory (thus I infer that the form.save worked ok), but then I get:
KeyError at /sourcedoc/create/
0
Request Method: POST
Request URL: http://www.rosshartshorn.net/worldmaker/sourcedoc/create/
Django Version: 1.4.3
It appears that my HttpResponseRedirect is, for whatever reason, not working, and it's trying to re-POST and throwing a KeyError off a blank form? Or something. In any event, it's not redirecting. When I manually go to /index/, all is well, and the new record is there.
Any ideas what is wrong with my redirect?
In case the forms are relevant:
<body>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<h1>New Post</h1>
<form enctype="multipart/form-data" action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Submit">
</form>
Also, I'm using mongoforms, which is supposed to work like ModelForms:
from mongodbforms import DocumentForm
class SourcedocForm(DocumentForm):
class Meta:
document = Sourcedoc
It seems to have been at least somewhat related to this post: Django MongodbForms KeyError _meta['cascade'].
So, I just upgraded to Django 1.5 and the newest mongodbforms. This eliminated that error, although I was then getting a problem similar to this:
MongoKit "ImportError: No module named objectid " error
I just implemented keppla's answer from that second post, and it now seems to work!

Include Django login form in base.html

I am new to Django and I would like to include my login form in base html.
I have the following:
"registration/login.html"
{% if form.has_errors %}
<p>Your username and password didn't match.
Please try again.</p>
{% endif %}
<form method="post" action=".">
{% csrf_token %}
<p><label>Username:</label>
{{ form.username }}</p>
<p><label>Password:</label>
{{ form.password }}</p>
<input type="submit" value="login" />
<input type="hidden" name="next" value="/" />
</form>
urls.py
url(r'^login/$', 'django.contrib.auth.views.login'),
And i included in base.html the following:
{% include "registration/login.html" %}
It renders everything except the textboxes for the username and password. I think I am missing something.
OK, the the problem, I think, is you're expecting "magic".
Django's views are templates are pretty dumb. They only have the variables available to them that are passed in one of two ways:
Through a context_processor (see Aidan's answer)
Passed in when calling render (or render_to_response)
The reason the form renders when you visit /login, but in no other case, is because the Django view you're using django.contrib.auth.views.login, passed the Django AuthenticationForm into your template for you.
Review the source here:
https://github.com/django/django/blob/master/django/contrib/auth/views.py
No other view "magically" gets that variable set.
So you have to either:
Set it yourself in every view across your whole app
Use a context processor.
Here's an easy context_processors.py which uses the Django stuff (based on Aidan's):
def include_login_form(request):
from django.contrib.auth.forms import AuthenticationForm
form = AuthenticationForm()
return {'login_form': form}
Then, do as Aidan points out:
TEMPLATE_CONTEXT_PROCESSORS = (
#....
'yourapp.context_processors.include_login_form'
)
Now, the problem is that, when you just include the template, the login form is in a variable called "login_form", but when django's built-in view uses your template, it's in a variable called "form".
So that's an ordeal without a good "clean" solution, though I'm sure one could sort it out.
Maybe check the context passed in in your context_processor, and if a variable already exists called "form", see if it's an instance of AuthenticationForm, and if it is, use it instead.
I sounds like the form isn't actually being passed to the template. If you want the login form to be included on every page then you probably want to add a context processor.
Create a context_processors.py file, something like -
def include_login_form(request):
form = YourLoginForm()
return {'login_form': form}
Then add your new context processor the TEMPLATE_CONTEXT_PROCESSORS setting -
TEMPLATE_CONTEXT_PROCESSORS = (
#....
'yourapp.context_proecssors.include_login_form',
)
This will mean that the context variable login_form is available to all your templates.