Add global CSRF protection

This commit is contained in:
Lance Edgar 2016-12-14 18:37:17 -06:00
parent ab09314ed3
commit 4ed522ae47
15 changed files with 28 additions and 22 deletions

View file

@ -129,6 +129,9 @@ def make_pyramid_config(settings):
config.set_authentication_policy(SessionAuthenticationPolicy())
config.set_authorization_policy(TailboneAuthorizationPolicy())
# always require CSRF token protection
config.set_default_csrf_options(require_csrf=True, token='_csrf')
# Bring in some Pyramid goodies.
config.include('pyramid_beaker')
config.include('pyramid_mako')

View file

@ -28,7 +28,7 @@ from __future__ import unicode_literals, absolute_import
from formencode import Schema
from .core import Form, Field, FieldSet, GenericFieldSet, invalid_csrf_token
from .core import Form, Field, FieldSet, GenericFieldSet
from .simpleform import SimpleForm, FormRenderer
from .alchemy import AlchemyForm
from .fields import AssociationProxyField

View file

@ -33,7 +33,6 @@ from pyramid.renderers import render
from webhelpers.html import HTML, tags
from tailbone.db import Session
from tailbone.forms import invalid_csrf_token
class TemplateEngine(fa.templates.TemplateEngine):
@ -114,8 +113,5 @@ class AlchemyForm(Object):
self.session.flush()
def validate(self):
if invalid_csrf_token(self.request):
self.request.session.flash("Invalid CSRF token", 'error')
return False
self.fieldset.rebind(data=self.request.params)
return self.fieldset.validate()

View file

@ -33,17 +33,6 @@ from formalchemy.helpers import content_tag
from pyramid.renderers import render
def invalid_csrf_token(request):
"""
Returns boolean indicating whether the given request has an *invalid* CSRF token.
"""
if request.method == 'POST':
csrf_token = request.session.get_csrf_token()
if request.POST.get('_csrf') != csrf_token:
return True
return False
class Form(object):
"""
Base class for all forms.

View file

@ -33,7 +33,7 @@ from pyramid_simpleform import renderers
from webhelpers.html import tags
from webhelpers.html import HTML
from tailbone.forms import Form, invalid_csrf_token
from tailbone.forms import Form
class SimpleForm(Form):
@ -53,9 +53,6 @@ class SimpleForm(Form):
return super(SimpleForm, self).render(**kwargs)
def validate(self):
if invalid_csrf_token(self.request):
self.request.session.flash("Invalid CSRF token", 'error')
return False
return self._form.validate()

View file

@ -34,7 +34,7 @@ from rattail.util import pretty_quantity
from webhelpers.html import *
from webhelpers.html.tags import *
from tailbone.util import pretty_datetime
from tailbone.util import csrf_token, pretty_datetime
def pretty_date(date):

View file

@ -5,6 +5,7 @@
<div class="form">
${h.form(url('change_password'))}
${form.csrf_token()}
${form.referrer_field()}
${form.field_div('current_password', form.password('current_password'))}
${form.field_div('new_password', form.password('new_password'))}

View file

@ -23,6 +23,7 @@
<div class="form">
${form.begin()}
${form.csrf_token()}
${form.hidden('user', value=request.user.uuid if request.user else None)}
<p>

View file

@ -36,6 +36,7 @@
<br />
${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="buttons">
<a class="button" href="${form.cancel_url}">Whoops, nevermind...</a>
<button type="button" id="confirm-delete">Yes, please DELETE this data forever!</button>

View file

@ -71,6 +71,7 @@ ${rows_grid|n}
<div id="execution-options-dialog" style="display: none;">
${h.form(url('{}.execute'.format(route_prefix), uuid=batch.uuid), name='batch-execution')}
${h.csrf_token(request)}
% if master.has_execution_options:
${rendered_execution_options|n}
% endif

View file

@ -32,6 +32,7 @@
<div class="form">
${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper">
<label for="batch_type">Batch Type</label>
@ -54,6 +55,7 @@
<div class="form">
${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper">
<label for="provider">Batch Type</label>

View file

@ -7,6 +7,7 @@
<br />
${h.form(request.current_route_url())}
${h.csrf_token(request)}
<div class="field-wrapper">
<label for="department">Department</label>

View file

@ -5,6 +5,7 @@
<div class="timesheet-wrapper">
${form.begin(id='filter-form')}
${form.csrf_token()}
<table class="timesheet-header">
<tbody>

View file

@ -283,6 +283,7 @@
<%def name="edit_form()">
${h.form(url('schedule.edit'), id='schedule-form')}
${h.csrf_token(request)}
</%def>
<%def name="edit_tools()">
@ -299,6 +300,7 @@ ${timesheet_wrapper(edit_form=edit_form, edit_tools=edit_tools, context_menu=con
${edit_tools()}
${h.form(url('schedule.edit'), id="clear-schedule-form")}
${h.csrf_token(request)}
${h.hidden('clear-schedule', value='clear')}
${h.end_form()}
@ -318,6 +320,7 @@ ${h.end_form()}
and then new shifts will be created based on the week you specify.
</p>
${h.form(url('schedule.edit'), id='copy-schedule-form')}
${h.csrf_token(request)}
<label for="copy-week">Copy from week:</label>
${h.text('copy-week')}
${h.end_form()}

View file

@ -31,11 +31,21 @@ import datetime
import pytz
import humanize
from webhelpers.html import HTML
from webhelpers.html import HTML, tags
from rattail.time import timezone, make_utc
def csrf_token(request, name='_csrf'):
"""
Convenience function. Returns CSRF hidden tag inside hidden DIV.
"""
token = request.session.get_csrf_token()
if token is None:
token = request.session.new_csrf_token()
return HTML.tag("div", tags.hidden(name, value=token), style="display:none;")
def pretty_datetime(config, value):
"""
Formats a datetime as a "pretty" human-readable string, with a tooltip