"""a module for patching django""" import imp import os import sys from django.utils.safestring import mark_safe from django.utils.functional import lazy from django.template import Node try: from functools import WRAPPER_ASSIGNMENTS except ImportError: from django.utils.functional import WRAPPER_ASSIGNMENTS def module_has_submodule(package, module_name): """See if 'module' is in 'package'.""" name = ".".join([package.__name__, module_name]) if name in sys.modules: return True for finder in sys.meta_path: if finder.find_module(name): return True for entry in package.__path__: # No __path__, then not a package. try: # Try the cached finder. finder = sys.path_importer_cache[entry] if finder is None: # Implicit import machinery should be used. try: file_, _, _ = imp.find_module(module_name, [entry]) if file_: file_.close() return True except ImportError: continue # Else see if the finder knows of a loader. elif finder.find_module(name): return True else: continue except KeyError: # No cached finder, so try and make one. for hook in sys.path_hooks: try: finder = hook(entry) # XXX Could cache in sys.path_importer_cache if finder.find_module(name): return True else: # Once a finder is found, stop the search. break except ImportError: # Continue the search for a finder. continue else: # No finder found. # Try the implicit import machinery if searching a directory. if os.path.isdir(entry): try: file_, _, _ = imp.find_module(module_name, [entry]) if file_: file_.close() return True except ImportError: pass # XXX Could insert None or NullImporter else: # Exhausted the search, so the module cannot be found. return False class CsrfTokenNode(Node): def render(self, context): csrf_token = context.get('csrf_token', None) if csrf_token: if csrf_token == 'NOTPROVIDED': return mark_safe(u"") else: return mark_safe(u"
" % csrf_token) else: # It's very probable that the token is missing because of # misconfiguration, so we raise a warning from django.conf import settings if settings.DEBUG: import warnings warnings.warn("A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.") return u'' def get_token(request): """ Returns the the CSRF token required for a POST form. A side effect of calling this function is to make the the csrf_protect decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' header to the outgoing response. For this reason, you may need to use this function lazily, as is done by the csrf context processor. """ request.META["CSRF_COOKIE_USED"] = True return request.META.get("CSRF_COOKIE", None) def csrf(request): """ Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if it has not been provided by either a view decorator or the middleware """ def _get_val(): token = get_token(request) if token is None: # In order to be able to provide debugging info in the # case of misconfiguration, we use a sentinel value # instead of returning an empty dict. return 'NOTPROVIDED' else: return token _get_val = lazy(_get_val, str) return {'csrf_token': _get_val() } """ Cross Site Request Forgery Middleware. This module provides a middleware that implements protection against request forgeries from other sites. """ import itertools import re import random from django.conf import settings from django.core.urlresolvers import get_callable from django.utils.hashcompat import md5_constructor from django.utils.safestring import mark_safe _POST_FORM_RE = \ re.compile(r'(]*\bmethod\s*=\s*(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) _HTML_TYPES = ('text/html', 'application/xhtml+xml') # Use the system (hardware-based) random number generator if it exists. if hasattr(random, 'SystemRandom'): randrange = random.SystemRandom().randrange else: randrange = random.randrange _MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 def _get_failure_view(): """ Returns the view to be used for CSRF rejections """ return get_callable(settings.CSRF_FAILURE_VIEW) def _get_new_csrf_key(): return md5_constructor("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() def _make_legacy_session_token(session_id): return md5_constructor(settings.SECRET_KEY + session_id).hexdigest() class CsrfViewMiddleware(object): """ Middleware that requires a present and correct csrfmiddlewaretoken for POST requests that have a CSRF cookie, and sets an outgoing CSRF cookie. This middleware should be used in conjunction with the csrf_token template tag. """ def process_view(self, request, callback, callback_args, callback_kwargs): if getattr(callback, 'csrf_exempt', False): return None if getattr(request, 'csrf_processing_done', False): return None reject = lambda s: _get_failure_view()(request, reason=s) def accept(): # Avoid checking the request twice by adding a custom attribute to # request. This will be relevant when both decorator and middleware # are used. request.csrf_processing_done = True return None # If the user doesn't have a CSRF cookie, generate one and store it in the # request, so it's available to the view. We'll store it in a cookie when # we reach the response. try: request.META["CSRF_COOKIE"] = request.COOKIES[settings.CSRF_COOKIE_NAME] cookie_is_new = False except KeyError: # No cookie, so create one. This will be sent with the next # response. request.META["CSRF_COOKIE"] = _get_new_csrf_key() # Set a flag to allow us to fall back and allow the session id in # place of a CSRF cookie for this request only. cookie_is_new = True if request.method == 'POST': if getattr(request, '_dont_enforce_csrf_checks', False): # Mechanism to turn off CSRF checks for test suite. It comes after # the creation of CSRF cookies, so that everything else continues to # work exactly the same (e.g. cookies are sent etc), but before the # any branches that call reject() return accept() if request.is_ajax(): # .is_ajax() is based on the presence of X-Requested-With. In # the context of a browser, this can only be sent if using # XmlHttpRequest. Browsers implement careful policies for # XmlHttpRequest: # # * Normally, only same-domain requests are allowed. # # * Some browsers (e.g. Firefox 3.5 and later) relax this # carefully: # # * if it is a 'simple' GET or POST request (which can # include no custom headers), it is allowed to be cross # domain. These requests will not be recognized as AJAX. # # * if a 'preflight' check with the server confirms that the # server is expecting and allows the request, cross domain # requests even with custom headers are allowed. These # requests will be recognized as AJAX, but can only get # through when the developer has specifically opted in to # allowing the cross-domain POST request. # # So in all cases, it is safe to allow these requests through. return accept() if request.is_secure(): # Strict referer checking for HTTPS referer = request.META.get('HTTP_REFERER') if referer is None: return reject("Referer checking failed - no Referer.") # The following check ensures that the referer is HTTPS, # the domains match and the ports match. This might be too strict. good_referer = 'https://%s/' % request.get_host() if not referer.startswith(good_referer): return reject("Referer checking failed - %s does not match %s." % (referer, good_referer)) # If the user didn't already have a CSRF cookie, then fall back to # the Django 1.1 method (hash of session ID), so a request is not # rejected if the form was sent to the user before upgrading to the # Django 1.2 method (session independent nonce) if cookie_is_new: try: session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] csrf_token = _make_legacy_session_token(session_id) except KeyError: # No CSRF cookie and no session cookie. For POST requests, # we insist on a CSRF cookie, and in this way we can avoid # all CSRF attacks, including login CSRF. return reject("No CSRF or session cookie.") else: csrf_token = request.META["CSRF_COOKIE"] # check incoming token request_csrf_token = request.POST.get('csrfmiddlewaretoken', None) if request_csrf_token != csrf_token: if cookie_is_new: # probably a problem setting the CSRF cookie return reject("CSRF cookie not set.") else: return reject("CSRF token missing or incorrect.") return accept() def process_response(self, request, response): if getattr(response, 'csrf_processing_done', False): return response # If CSRF_COOKIE is unset, then CsrfViewMiddleware.process_view was # never called, probaby because a request middleware returned a response # (for example, contrib.auth redirecting to a login page). if request.META.get("CSRF_COOKIE") is None: return response if not request.META.get("CSRF_COOKIE_USED", False): return response # Set the CSRF cookie even if it's already set, so we renew the expiry timer. response.set_cookie(settings.CSRF_COOKIE_NAME, request.META["CSRF_COOKIE"], max_age = 60 * 60 * 24 * 7 * 52, domain=settings.CSRF_COOKIE_DOMAIN) # Content varies with the CSRF cookie, so set the Vary header. from django.utils.cache import patch_vary_headers patch_vary_headers(response, ('Cookie',)) response.csrf_processing_done = True return response from django.utils.decorators import decorator_from_middleware from functools import wraps csrf_protect = decorator_from_middleware(CsrfViewMiddleware) csrf_protect.__name__ = "csrf_protect" csrf_protect.__doc__ = """ This decorator adds CSRF protection in exactly the same way as CsrfViewMiddleware, but it can be used on a per view basis. Using both, or using the decorator multiple times, is harmless and efficient. """ def add_import_library_function(): #this definition is copy/pasted from django 1.2 source code #it is necessary to make Coffin library happy from django.utils.importlib import import_module class InvalidTemplateLibrary(Exception): pass def import_library(taglib_module): """Load a template tag library module. Verifies that the library contains a 'register' attribute, and returns that attribute as the representation of the library """ app_path, taglib = taglib_module.rsplit('.',1) app_module = import_module(app_path) try: mod = import_module(taglib_module) except ImportError, e: # If the ImportError is because the taglib submodule does not exist, that's not # an error that should be raised. If the submodule exists and raised an ImportError # on the attempt to load it, that we want to raise. if not module_has_submodule(app_module, taglib): return None else: raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e)) try: return mod.register except AttributeError: raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module) import django.template django.template.import_library = import_library def add_csrf_protection(): """adds csrf_token template tag to django Must be used if version of django is < 1.2 Also adds csrf function to the context processor and the csrf_protect decorator for the views """ import django.template.defaulttags def csrf_token(parser, token): return CsrfTokenNode() django.template.defaulttags.CsrfTokenNode = CsrfTokenNode django.template.defaulttags.register.tag(csrf_token) #add csrf context processor import django.core.context_processors django.core.context_processors.csrf = csrf #add csrf_protect decorator import django.views.decorators django.views.decorators.csrf = imp.new_module('csrf') django.views.decorators.csrf.csrf_protect = csrf_protect def add_available_attrs_decorator(): def available_attrs(fn): """ Return the list of functools-wrappable attributes on a callable. This is required as a workaround for http://bugs.python.org/issue3445. """ return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a)) import django.utils.decorators django.utils.decorators.available_attrs = available_attrs def add_render_shortcut(): """adds `render` shortcut, introduced with django 1.3""" try: from django.shortcuts import render except ImportError: def render(request, template, data=None): from django.shortcuts import render_to_response from django.template import RequestContext return render_to_response(template, RequestContext(request, data)) import django.shortcuts django.shortcuts.render = render