app/django/contrib/csrf/middleware.py
author Lennard de Rijk <ljvderijk@gmail.com>
Thu, 13 Aug 2009 14:02:22 -0700
changeset 2770 71a5a56cf29e
parent 323 ff1a9aa48cfd
permissions -rw-r--r--
Redone the acceptedStudentsExport functionality. The method has been renamed to exportStudentsWithProjects and retrieves the document_name and the new shipping address properties. Also it filters out all invalid projects or projects for wich the scope_path doesn't match the given scope_pa th_start. Also there is no more need to use the argument given to this method when adding the extra columns. The data is now prepared by one loop which uses the key present in the accepted_students dictionary for retrieving the data used by the extra columns.

"""
Cross Site Request Forgery Middleware.

This module provides a middleware that implements protection
against request forgeries from other sites.
"""

import re
import itertools

from django.conf import settings
from django.http import HttpResponseForbidden
from django.utils.hashcompat import md5_constructor
from django.utils.safestring import mark_safe

_ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>')

_POST_FORM_RE = \
    re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)

_HTML_TYPES = ('text/html', 'application/xhtml+xml')

def _make_token(session_id):
    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()

class CsrfMiddleware(object):
    """Django middleware that adds protection against Cross Site
    Request Forgeries by adding hidden form fields to POST forms and
    checking requests for the correct value.

    In the list of middlewares, SessionMiddleware is required, and must come
    after this middleware.  CsrfMiddleWare must come after compression
    middleware.

    If a session ID cookie is present, it is hashed with the SECRET_KEY
    setting to create an authentication token.  This token is added to all
    outgoing POST forms and is expected on all incoming POST requests that
    have a session ID cookie.

    If you are setting cookies directly, instead of using Django's session
    framework, this middleware will not work.
    """

    def process_request(self, request):
        if request.method == 'POST':
            try:
                session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
            except KeyError:
                # No session, no check required
                return None

            csrf_token = _make_token(session_id)
            # check incoming token
            try:
                request_csrf_token = request.POST['csrfmiddlewaretoken']
            except KeyError:
                return HttpResponseForbidden(_ERROR_MSG)

            if request_csrf_token != csrf_token:
                return HttpResponseForbidden(_ERROR_MSG)

        return None

    def process_response(self, request, response):
        csrf_token = None
        try:
            cookie = response.cookies[settings.SESSION_COOKIE_NAME]
            csrf_token = _make_token(cookie.value)
        except KeyError:
            # No outgoing cookie to set session, but
            # a session might already exist.
            try:
                session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
                csrf_token = _make_token(session_id)
            except KeyError:
                # no incoming or outgoing cookie
                pass

        if csrf_token is not None and \
                response['Content-Type'].split(';')[0] in _HTML_TYPES:

            # ensure we don't add the 'id' attribute twice (HTML validity)
            idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
                                            itertools.repeat(''))
            def add_csrf_field(match):
                """Returns the matched <form> tag plus the added <input> element"""
                return mark_safe(match.group() + "<div style='display:none;'>" + \
                "<input type='hidden' " + idattributes.next() + \
                " name='csrfmiddlewaretoken' value='" + csrf_token + \
                "' /></div>")

            # Modify any POST forms
            response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
        return response