import uuid
from ferris.core.controller import add_authorizations
[docs]class CSRF(object):
"""
Provides protection against `Cross-site Request Forgery <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_.
Example::
class Pages(Controller):
class Meta:
components = (Scaffolding, CSRF)
admin_list = scaffold.list
@csrf_protect
def admin_add(self):
return scaffold.add(self)
Scaffold will automatically ensure the csrf token is part of the form. For non-scaffold forms you
will need to add {{csrf}} inside of your form.
"""
def __init__(self, controller):
self.controller = controller
self.controller.events.before_render += self._on_before_render
self.controller.meta.view.events.before_form_fields += self._on_before_form_fields
def _on_before_render(self, controller, *args, **kwargs):
controller.context['csrf'] = create_csrf_field(generate_csrf_token(controller))
def _on_before_form_fields(self):
return self.controller.context.get('csrf')
def generate_csrf_token(controller):
""" Generates a new csrf token and stores it in the session"""
session = controller.session
if '_csrf_token' not in session:
session['_csrf_token'] = uuid.uuid4()
return session['_csrf_token']
def create_csrf_field(token):
return '<input type="hidden" name="csrf_token" value="%s">' % token
def require_csrf(controller):
"""
Authorization chain that validates the CSRF token.
"""
if controller.request.method in ('POST', 'PUT') and not controller.request.path.startswith('/taskqueue'):
token = controller.session.get('_csrf_token')
if not token or str(token) != str(controller.request.get('csrf_token')):
return False, "Cross-site request forgery failure"
return True
[docs]def csrf_protect(f):
"""
Shortcut decorator to easily add the CSRF check to an action
"""
return add_authorizations(require_csrf)(f)