web/lib/django/middleware/csrf.py
author ymh <ymh.work@gmail.com>
Thu, 05 Aug 2010 17:28:09 +0200
changeset 50 012451a812f1
parent 29 cc9b7e14412b
permissions -rw-r--r--
Merge with a2711e44ba5de8b1675d7e0ee6aaa4a6c56a9b46
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
29
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
Cross Site Request Forgery Middleware.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
This module provides a middleware that implements protection
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
against request forgeries from other sites.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
"""
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
import itertools
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
import re
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
import random
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
from django.conf import settings
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
from django.core.urlresolvers import get_callable
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
from django.utils.cache import patch_vary_headers
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
from django.utils.hashcompat import md5_constructor
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
from django.utils.safestring import mark_safe
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
_POST_FORM_RE = \
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
    re.compile(r'(<form\W[^>]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
_HTML_TYPES = ('text/html', 'application/xhtml+xml')
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
# Use the system (hardware-based) random number generator if it exists.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
if hasattr(random, 'SystemRandom'):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
    randrange = random.SystemRandom().randrange
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
else:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
    randrange = random.randrange
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
_MAX_CSRF_KEY = 18446744073709551616L     # 2 << 63
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
def _get_failure_view():
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
    Returns the view to be used for CSRF rejections
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
    return get_callable(settings.CSRF_FAILURE_VIEW)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
def _get_new_csrf_key():
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
    return md5_constructor("%s%s"
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
                % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
def _make_legacy_session_token(session_id):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
    return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
def get_token(request):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    Returns the the CSRF token required for a POST form.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
    A side effect of calling this function is to make the the csrf_protect
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie'
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    header to the outgoing response.  For this reason, you may need to use this
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
    function lazily, as is done by the csrf context processor.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
    request.META["CSRF_COOKIE_USED"] = True
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
    return request.META.get("CSRF_COOKIE", None)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
class CsrfViewMiddleware(object):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
    Middleware that requires a present and correct csrfmiddlewaretoken
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    for POST requests that have a CSRF cookie, and sets an outgoing
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    CSRF cookie.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    This middleware should be used in conjunction with the csrf_token template
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
    tag.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    def process_view(self, request, callback, callback_args, callback_kwargs):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        if getattr(callback, 'csrf_exempt', False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
            return None
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        if getattr(request, 'csrf_processing_done', False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
            return None
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        reject = lambda s: _get_failure_view()(request, reason=s)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        def accept():
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
            # Avoid checking the request twice by adding a custom attribute to
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
            # request.  This will be relevant when both decorator and middleware
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
            # are used.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
            request.csrf_processing_done = True
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
            return None
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
        # If the user doesn't have a CSRF cookie, generate one and store it in the
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
        # request, so it's available to the view.  We'll store it in a cookie when
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
        # we reach the response.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
        try:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
            request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME]
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
            cookie_is_new = False
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        except KeyError:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
            # No cookie, so create one.  This will be sent with the next
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
            # response.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
            request.META["CSRF_COOKIE"] = _get_new_csrf_key()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
            # Set a flag to allow us to fall back and allow the session id in
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
            # place of a CSRF cookie for this request only.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
            cookie_is_new = True
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
        if request.method == 'POST':
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
            if getattr(request, '_dont_enforce_csrf_checks', False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
                # Mechanism to turn off CSRF checks for test suite.  It comes after
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
                # the creation of CSRF cookies, so that everything else continues to
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
                # work exactly the same (e.g. cookies are sent etc), but before the
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
                # any branches that call reject()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
                return accept()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
            if request.is_ajax():
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
                # .is_ajax() is based on the presence of X-Requested-With.  In
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
                # the context of a browser, this can only be sent if using
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
                # XmlHttpRequest.  Browsers implement careful policies for
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
                # XmlHttpRequest:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
                #
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
                #  * Normally, only same-domain requests are allowed.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
                #
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
                #  * Some browsers (e.g. Firefox 3.5 and later) relax this
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
                #    carefully:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
                #
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
                #    * if it is a 'simple' GET or POST request (which can
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
                #      include no custom headers), it is allowed to be cross
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
                #      domain.  These requests will not be recognized as AJAX.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
                #
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
                #    * if a 'preflight' check with the server confirms that the
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
                #      server is expecting and allows the request, cross domain
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
                #      requests even with custom headers are allowed. These
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
                #      requests will be recognized as AJAX, but can only get
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
                #      through when the developer has specifically opted in to
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
                #      allowing the cross-domain POST request.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
                #
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
                # So in all cases, it is safe to allow these requests through.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
                return accept()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
            if request.is_secure():
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
                # Strict referer checking for HTTPS
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
                referer = request.META.get('HTTP_REFERER')
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
                if referer is None:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
                    return reject("Referer checking failed - no Referer.")
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
                # The following check ensures that the referer is HTTPS,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
                # the domains match and the ports match.  This might be too strict.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
                good_referer = 'https://%s/' % request.get_host()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
                if not referer.startswith(good_referer):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
                    return reject("Referer checking failed - %s does not match %s." %
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
                                  (referer, good_referer))
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
            # If the user didn't already have a CSRF cookie, then fall back to
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
            # the Django 1.1 method (hash of session ID), so a request is not
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
            # rejected if the form was sent to the user before upgrading to the
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
            # Django 1.2 method (session independent nonce)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
            if cookie_is_new:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
                try:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
                    session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
                    csrf_token = _make_legacy_session_token(session_id)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
                except KeyError:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
                    # No CSRF cookie and no session cookie. For POST requests,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
                    # we insist on a CSRF cookie, and in this way we can avoid
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
                    # all CSRF attacks, including login CSRF.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
                    return reject("No CSRF or session cookie.")
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
            else:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
                csrf_token = request.META["CSRF_COOKIE"]
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
            # check incoming token
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
            request_csrf_token = request.POST.get('csrfmiddlewaretoken', None)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
            if request_csrf_token != csrf_token:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
                if cookie_is_new:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
                    # probably a problem setting the CSRF cookie
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
                    return reject("CSRF cookie not set.")
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
                else:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
                    return reject("CSRF token missing or incorrect.")
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
        return accept()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
    def process_response(self, request, response):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
        if getattr(response, 'csrf_processing_done', False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
            return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
        # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
        # never called, probaby because a request middleware returned a response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
        # (for example, contrib.auth redirecting to a login page).
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
        if request.META.get("CSRF_COOKIE") is None:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
            return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
        if not request.META.get("CSRF_COOKIE_USED", False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
            return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
        # Set the CSRF cookie even if it's already set, so we renew the expiry timer.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
        response.set_cookie(settings.CSRF_COOKIE_NAME,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
                request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
                domain=settings.CSRF_COOKIE_DOMAIN)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
        # Content varies with the CSRF cookie, so set the Vary header.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
        patch_vary_headers(response, ('Cookie',))
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
        response.csrf_processing_done = True
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
class CsrfResponseMiddleware(object):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
    DEPRECATED
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
    Middleware that post-processes a response to add a csrfmiddlewaretoken.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
    This exists for backwards compatibility and as an interim measure until
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
    applications are converted to using use the csrf_token template tag
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
    instead. It will be removed in Django 1.4.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
    def __init__(self):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
        import warnings
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
        warnings.warn(
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
            "CsrfResponseMiddleware and CsrfMiddleware are deprecated; use CsrfViewMiddleware and the template tag instead (see CSRF documentation).",
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
            PendingDeprecationWarning
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
        )
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
    def process_response(self, request, response):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
        if getattr(response, 'csrf_exempt', False):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
            return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
        if response['Content-Type'].split(';')[0] in _HTML_TYPES:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
            csrf_token = get_token(request)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
            # If csrf_token is None, we have no token for this request, which probably
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
            # means that this is a response from a request middleware.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
            if csrf_token is None:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
                return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
            # ensure we don't add the 'id' attribute twice (HTML validity)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
            idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
                                           itertools.repeat(''))
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
            def add_csrf_field(match):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
                """Returns the matched <form> tag plus the added <input> element"""
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
                return mark_safe(match.group() + "<div style='display:none;'>" + \
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
                "<input type='hidden' " + idattributes.next() + \
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
                " name='csrfmiddlewaretoken' value='" + csrf_token + \
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
                "' /></div>")
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
            # Modify any POST forms
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
            response.content, n = _POST_FORM_RE.subn(add_csrf_field, response.content)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
            if n > 0:
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
                # Content varies with the CSRF cookie, so set the Vary header.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
                patch_vary_headers(response, ('Cookie',))
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
                # Since the content has been modified, any Etag will now be
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
                # incorrect.  We could recalculate, but only if we assume that
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
                # the Etag was set by CommonMiddleware. The safest thing is just
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
                # to delete. See bug #9163
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
                del response['ETag']
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
        return response
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
class CsrfMiddleware(object):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
    Django middleware that adds protection against Cross Site
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
    Request Forgeries by adding hidden form fields to POST forms and
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
    checking requests for the correct value.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
    CsrfMiddleware uses two middleware, CsrfViewMiddleware and
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
    CsrfResponseMiddleware, which can be used independently.  It is recommended
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
    to use only CsrfViewMiddleware and use the csrf_token template tag in
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
    templates for inserting the token.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
    """
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
    # We can't just inherit from CsrfViewMiddleware and CsrfResponseMiddleware
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
    # because both have process_response methods.
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
    def __init__(self):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
        self.response_middleware = CsrfResponseMiddleware()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
        self.view_middleware = CsrfViewMiddleware()
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
    def process_response(self, request, resp):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
        # We must do the response post-processing first, because that calls
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
        # get_token(), which triggers a flag saying that the CSRF cookie needs
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
        # to be sent (done in CsrfViewMiddleware.process_response)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
        resp2 = self.response_middleware.process_response(request, resp)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
        return self.view_middleware.process_response(request, resp2)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
    def process_view(self, request, callback, callback_args, callback_kwargs):
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
        return self.view_middleware.process_view(request, callback, callback_args,
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
                                                 callback_kwargs)
cc9b7e14412b update django and lucene
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265