diff options
Diffstat (limited to 'django/middleware')
| -rw-r--r-- | django/middleware/cache.py | 40 | ||||
| -rw-r--r-- | django/middleware/clickjacking.py | 9 | ||||
| -rw-r--r-- | django/middleware/common.py | 53 | ||||
| -rw-r--r-- | django/middleware/csrf.py | 137 | ||||
| -rw-r--r-- | django/middleware/gzip.py | 19 | ||||
| -rw-r--r-- | django/middleware/http.py | 17 | ||||
| -rw-r--r-- | django/middleware/locale.py | 58 | ||||
| -rw-r--r-- | django/middleware/security.py | 33 |
8 files changed, 210 insertions, 156 deletions
diff --git a/django/middleware/cache.py b/django/middleware/cache.py index c191118183..0fdffe1bbe 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -46,7 +46,10 @@ More details about how the caching works: from django.conf import settings from django.core.cache import DEFAULT_CACHE_ALIAS, caches from django.utils.cache import ( - get_cache_key, get_max_age, has_vary_header, learn_cache_key, + get_cache_key, + get_max_age, + has_vary_header, + learn_cache_key, patch_response_headers, ) from django.utils.deprecation import MiddlewareMixin @@ -61,6 +64,7 @@ class UpdateCacheMiddleware(MiddlewareMixin): UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE so that it'll get called last during the response phase. """ + def __init__(self, get_response): super().__init__(get_response) self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS @@ -73,7 +77,7 @@ class UpdateCacheMiddleware(MiddlewareMixin): return caches[self.cache_alias] def _should_update_cache(self, request, response): - return hasattr(request, '_cache_update_cache') and request._cache_update_cache + return hasattr(request, "_cache_update_cache") and request._cache_update_cache def process_response(self, request, response): """Set the cache, if needed.""" @@ -86,11 +90,15 @@ class UpdateCacheMiddleware(MiddlewareMixin): # Don't cache responses that set a user-specific (and maybe security # sensitive) cookie in response to a cookie-less request. - if not request.COOKIES and response.cookies and has_vary_header(response, 'Cookie'): + if ( + not request.COOKIES + and response.cookies + and has_vary_header(response, "Cookie") + ): return response # Don't cache a response with 'Cache-Control: private' - if 'private' in response.get('Cache-Control', ()): + if "private" in response.get("Cache-Control", ()): return response # Page timeout takes precedence over the "max-age" and the default @@ -107,8 +115,10 @@ class UpdateCacheMiddleware(MiddlewareMixin): return response patch_response_headers(response, timeout) if timeout and response.status_code == 200: - cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache) - if hasattr(response, 'render') and callable(response.render): + cache_key = learn_cache_key( + request, response, timeout, self.key_prefix, cache=self.cache + ) + if hasattr(response, "render") and callable(response.render): response.add_post_render_callback( lambda r: self.cache.set(cache_key, r, timeout) ) @@ -125,6 +135,7 @@ class FetchFromCacheMiddleware(MiddlewareMixin): FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE so that it'll get called last during the request phase. """ + def __init__(self, get_response): super().__init__(get_response) self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX @@ -139,19 +150,21 @@ class FetchFromCacheMiddleware(MiddlewareMixin): Check whether the page is already cached and return the cached version if available. """ - if request.method not in ('GET', 'HEAD'): + if request.method not in ("GET", "HEAD"): request._cache_update_cache = False return None # Don't bother checking the cache. # try and get the cached GET response - cache_key = get_cache_key(request, self.key_prefix, 'GET', cache=self.cache) + cache_key = get_cache_key(request, self.key_prefix, "GET", cache=self.cache) if cache_key is None: request._cache_update_cache = True return None # No cache information available, need to rebuild. response = self.cache.get(cache_key) # if it wasn't found and we are looking for a HEAD, try looking just for that - if response is None and request.method == 'HEAD': - cache_key = get_cache_key(request, self.key_prefix, 'HEAD', cache=self.cache) + if response is None and request.method == "HEAD": + cache_key = get_cache_key( + request, self.key_prefix, "HEAD", cache=self.cache + ) response = self.cache.get(cache_key) if response is None: @@ -170,6 +183,7 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): Also used as the hook point for the cache decorator, which is generated using the decorator-from-middleware utility. """ + def __init__(self, get_response, cache_timeout=None, page_timeout=None, **kwargs): super().__init__(get_response) # We need to differentiate between "provided, but using default value", @@ -178,14 +192,14 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): # we need to use middleware defaults. try: - key_prefix = kwargs['key_prefix'] + key_prefix = kwargs["key_prefix"] if key_prefix is None: - key_prefix = '' + key_prefix = "" self.key_prefix = key_prefix except KeyError: pass try: - cache_alias = kwargs['cache_alias'] + cache_alias = kwargs["cache_alias"] if cache_alias is None: cache_alias = DEFAULT_CACHE_ALIAS self.cache_alias = cache_alias diff --git a/django/middleware/clickjacking.py b/django/middleware/clickjacking.py index 0161f8eb8f..072f4148a2 100644 --- a/django/middleware/clickjacking.py +++ b/django/middleware/clickjacking.py @@ -21,16 +21,17 @@ class XFrameOptionsMiddleware(MiddlewareMixin): response from being loaded in a frame in any site, set X_FRAME_OPTIONS in your project's Django settings to 'DENY'. """ + def process_response(self, request, response): # Don't set it if it's already in the response - if response.get('X-Frame-Options') is not None: + if response.get("X-Frame-Options") is not None: return response # Don't set it if they used @xframe_options_exempt - if getattr(response, 'xframe_options_exempt', False): + if getattr(response, "xframe_options_exempt", False): return response - response.headers['X-Frame-Options'] = self.get_xframe_options_value( + response.headers["X-Frame-Options"] = self.get_xframe_options_value( request, response, ) @@ -44,4 +45,4 @@ class XFrameOptionsMiddleware(MiddlewareMixin): This method can be overridden if needed, allowing it to vary based on the request or response. """ - return getattr(settings, 'X_FRAME_OPTIONS', 'DENY').upper() + return getattr(settings, "X_FRAME_OPTIONS", "DENY").upper() diff --git a/django/middleware/common.py b/django/middleware/common.py index e42d05e255..c652374aec 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -38,16 +38,16 @@ class CommonMiddleware(MiddlewareMixin): """ # Check for denied User-Agents - user_agent = request.META.get('HTTP_USER_AGENT') + user_agent = request.META.get("HTTP_USER_AGENT") if user_agent is not None: for user_agent_regex in settings.DISALLOWED_USER_AGENTS: if user_agent_regex.search(user_agent): - raise PermissionDenied('Forbidden user agent') + raise PermissionDenied("Forbidden user agent") # Check for a redirect based on settings.PREPEND_WWW host = request.get_host() - must_prepend = settings.PREPEND_WWW and host and not host.startswith('www.') - redirect_url = ('%s://www.%s' % (request.scheme, host)) if must_prepend else '' + must_prepend = settings.PREPEND_WWW and host and not host.startswith("www.") + redirect_url = ("%s://www.%s" % (request.scheme, host)) if must_prepend else "" # Check if a slash should be appended if self.should_redirect_with_slash(request): @@ -65,13 +65,13 @@ class CommonMiddleware(MiddlewareMixin): Return True if settings.APPEND_SLASH is True and appending a slash to the request path turns an invalid path into a valid one. """ - if settings.APPEND_SLASH and not request.path_info.endswith('/'): - urlconf = getattr(request, 'urlconf', None) + if settings.APPEND_SLASH and not request.path_info.endswith("/"): + urlconf = getattr(request, "urlconf", None) if not is_valid_path(request.path_info, urlconf): - match = is_valid_path('%s/' % request.path_info, urlconf) + match = is_valid_path("%s/" % request.path_info, urlconf) if match: view = match.func - return getattr(view, 'should_append_slash', True) + return getattr(view, "should_append_slash", True) return False def get_full_path_with_slash(self, request): @@ -84,15 +84,16 @@ class CommonMiddleware(MiddlewareMixin): new_path = request.get_full_path(force_append_slash=True) # Prevent construction of scheme relative urls. new_path = escape_leading_slashes(new_path) - if settings.DEBUG and request.method in ('POST', 'PUT', 'PATCH'): + if settings.DEBUG and request.method in ("POST", "PUT", "PATCH"): raise RuntimeError( "You called this URL via %(method)s, but the URL doesn't end " "in a slash and you have APPEND_SLASH set. Django can't " "redirect to the slash URL while maintaining %(method)s data. " "Change your form to point to %(url)s (note the trailing " - "slash), or set APPEND_SLASH=False in your Django settings." % { - 'method': request.method, - 'url': request.get_host() + new_path, + "slash), or set APPEND_SLASH=False in your Django settings." + % { + "method": request.method, + "url": request.get_host() + new_path, } ) return new_path @@ -109,28 +110,32 @@ class CommonMiddleware(MiddlewareMixin): # Add the Content-Length header to non-streaming responses if not # already set. - if not response.streaming and not response.has_header('Content-Length'): - response.headers['Content-Length'] = str(len(response.content)) + if not response.streaming and not response.has_header("Content-Length"): + response.headers["Content-Length"] = str(len(response.content)) return response class BrokenLinkEmailsMiddleware(MiddlewareMixin): - def process_response(self, request, response): """Send broken link emails for relevant 404 NOT FOUND responses.""" if response.status_code == 404 and not settings.DEBUG: domain = request.get_host() path = request.get_full_path() - referer = request.META.get('HTTP_REFERER', '') + referer = request.META.get("HTTP_REFERER", "") if not self.is_ignorable_request(request, path, domain, referer): - ua = request.META.get('HTTP_USER_AGENT', '<none>') - ip = request.META.get('REMOTE_ADDR', '<none>') + ua = request.META.get("HTTP_USER_AGENT", "<none>") + ip = request.META.get("REMOTE_ADDR", "<none>") mail_managers( - "Broken %slink on %s" % ( - ('INTERNAL ' if self.is_internal_request(domain, referer) else ''), - domain + "Broken %slink on %s" + % ( + ( + "INTERNAL " + if self.is_internal_request(domain, referer) + else "" + ), + domain, ), "Referrer: %s\nRequested URL: %s\nUser agent: %s\n" "IP address: %s\n" % (referer, path, ua, ip), @@ -158,17 +163,17 @@ class BrokenLinkEmailsMiddleware(MiddlewareMixin): # APPEND_SLASH is enabled and the referer is equal to the current URL # without a trailing slash indicating an internal redirect. - if settings.APPEND_SLASH and uri.endswith('/') and referer == uri[:-1]: + if settings.APPEND_SLASH and uri.endswith("/") and referer == uri[:-1]: return True # A '?' in referer is identified as a search engine source. - if not self.is_internal_request(domain, referer) and '?' in referer: + if not self.is_internal_request(domain, referer) and "?" in referer: return True # The referer is equal to the current URL, ignoring the scheme (assumed # to be a poorly implemented bot). parsed_referer = urlparse(referer) - if parsed_referer.netloc in ['', domain] and parsed_referer.path == uri: + if parsed_referer.netloc in ["", domain] and parsed_referer.path == uri: return True return any(pattern.search(uri) for pattern in settings.IGNORABLE_404_URLS) diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index 6be68ebd76..94f580fa71 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -22,27 +22,29 @@ from django.utils.http import is_same_domain from django.utils.log import log_response from django.utils.regex_helper import _lazy_re_compile -logger = logging.getLogger('django.security.csrf') +logger = logging.getLogger("django.security.csrf") # This matches if any character is not in CSRF_ALLOWED_CHARS. -invalid_token_chars_re = _lazy_re_compile('[^a-zA-Z0-9]') +invalid_token_chars_re = _lazy_re_compile("[^a-zA-Z0-9]") REASON_BAD_ORIGIN = "Origin checking failed - %s does not match any trusted origins." REASON_NO_REFERER = "Referer checking failed - no Referer." REASON_BAD_REFERER = "Referer checking failed - %s does not match any trusted origins." REASON_NO_CSRF_COOKIE = "CSRF cookie not set." -REASON_CSRF_TOKEN_MISSING = 'CSRF token missing.' +REASON_CSRF_TOKEN_MISSING = "CSRF token missing." REASON_MALFORMED_REFERER = "Referer checking failed - Referer is malformed." -REASON_INSECURE_REFERER = "Referer checking failed - Referer is insecure while host is secure." +REASON_INSECURE_REFERER = ( + "Referer checking failed - Referer is insecure while host is secure." +) # The reason strings below are for passing to InvalidTokenFormat. They are # phrases without a subject because they can be in reference to either the CSRF # cookie or non-cookie token. -REASON_INCORRECT_LENGTH = 'has incorrect length' -REASON_INVALID_CHARACTERS = 'has invalid characters' +REASON_INCORRECT_LENGTH = "has incorrect length" +REASON_INVALID_CHARACTERS = "has invalid characters" CSRF_SECRET_LENGTH = 32 CSRF_TOKEN_LENGTH = 2 * CSRF_SECRET_LENGTH CSRF_ALLOWED_CHARS = string.ascii_letters + string.digits -CSRF_SESSION_KEY = '_csrftoken' +CSRF_SESSION_KEY = "_csrftoken" def _get_failure_view(): @@ -62,7 +64,7 @@ def _mask_cipher_secret(secret): mask = _get_new_csrf_string() chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in secret), (chars.index(x) for x in mask)) - cipher = ''.join(chars[(x + y) % len(chars)] for x, y in pairs) + cipher = "".join(chars[(x + y) % len(chars)] for x, y in pairs) return mask + cipher @@ -76,21 +78,24 @@ def _unmask_cipher_token(token): token = token[CSRF_SECRET_LENGTH:] chars = CSRF_ALLOWED_CHARS pairs = zip((chars.index(x) for x in token), (chars.index(x) for x in mask)) - return ''.join(chars[x - y] for x, y in pairs) # Note negative values are ok + return "".join(chars[x - y] for x, y in pairs) # Note negative values are ok def _add_new_csrf_cookie(request): """Generate a new random CSRF_COOKIE value, and add it to request.META.""" csrf_secret = _get_new_csrf_string() - request.META.update({ - # RemovedInDjango50Warning: when the deprecation ends, replace - # with: 'CSRF_COOKIE': csrf_secret - 'CSRF_COOKIE': ( - _mask_cipher_secret(csrf_secret) - if settings.CSRF_COOKIE_MASKED else csrf_secret - ), - 'CSRF_COOKIE_NEEDS_UPDATE': True, - }) + request.META.update( + { + # RemovedInDjango50Warning: when the deprecation ends, replace + # with: 'CSRF_COOKIE': csrf_secret + "CSRF_COOKIE": ( + _mask_cipher_secret(csrf_secret) + if settings.CSRF_COOKIE_MASKED + else csrf_secret + ), + "CSRF_COOKIE_NEEDS_UPDATE": True, + } + ) return csrf_secret @@ -104,12 +109,12 @@ def get_token(request): header to the outgoing response. For this reason, you may need to use this function lazily, as is done by the csrf context processor. """ - if 'CSRF_COOKIE' in request.META: - csrf_secret = request.META['CSRF_COOKIE'] + if "CSRF_COOKIE" in request.META: + csrf_secret = request.META["CSRF_COOKIE"] # Since the cookie is being used, flag to send the cookie in # process_response() (even if the client already has it) in order to # renew the expiry timer. - request.META['CSRF_COOKIE_NEEDS_UPDATE'] = True + request.META["CSRF_COOKIE_NEEDS_UPDATE"] = True else: csrf_secret = _add_new_csrf_cookie(request) return _mask_cipher_secret(csrf_secret) @@ -171,19 +176,17 @@ class CsrfViewMiddleware(MiddlewareMixin): This middleware should be used in conjunction with the {% csrf_token %} template tag. """ + @cached_property def csrf_trusted_origins_hosts(self): return [ - urlparse(origin).netloc.lstrip('*') + urlparse(origin).netloc.lstrip("*") for origin in settings.CSRF_TRUSTED_ORIGINS ] @cached_property def allowed_origins_exact(self): - return { - origin for origin in settings.CSRF_TRUSTED_ORIGINS - if '*' not in origin - } + return {origin for origin in settings.CSRF_TRUSTED_ORIGINS if "*" not in origin} @cached_property def allowed_origin_subdomains(self): @@ -192,8 +195,12 @@ class CsrfViewMiddleware(MiddlewareMixin): subdomains of the netloc are allowed. """ allowed_origin_subdomains = defaultdict(list) - for parsed in (urlparse(origin) for origin in settings.CSRF_TRUSTED_ORIGINS if '*' in origin): - allowed_origin_subdomains[parsed.scheme].append(parsed.netloc.lstrip('*')) + for parsed in ( + urlparse(origin) + for origin in settings.CSRF_TRUSTED_ORIGINS + if "*" in origin + ): + allowed_origin_subdomains[parsed.scheme].append(parsed.netloc.lstrip("*")) return allowed_origin_subdomains # The _accept and _reject methods currently only exist for the sake of the @@ -208,7 +215,9 @@ class CsrfViewMiddleware(MiddlewareMixin): def _reject(self, request, reason): response = _get_failure_view()(request, reason=reason) log_response( - 'Forbidden (%s): %s', reason, request.path, + "Forbidden (%s): %s", + reason, + request.path, response=response, request=request, logger=logger, @@ -228,9 +237,9 @@ class CsrfViewMiddleware(MiddlewareMixin): csrf_secret = request.session.get(CSRF_SESSION_KEY) except AttributeError: raise ImproperlyConfigured( - 'CSRF_USE_SESSIONS is enabled, but request.session is not ' - 'set. SessionMiddleware must appear before CsrfViewMiddleware ' - 'in MIDDLEWARE.' + "CSRF_USE_SESSIONS is enabled, but request.session is not " + "set. SessionMiddleware must appear before CsrfViewMiddleware " + "in MIDDLEWARE." ) else: try: @@ -249,12 +258,12 @@ class CsrfViewMiddleware(MiddlewareMixin): def _set_csrf_cookie(self, request, response): if settings.CSRF_USE_SESSIONS: - if request.session.get(CSRF_SESSION_KEY) != request.META['CSRF_COOKIE']: - request.session[CSRF_SESSION_KEY] = request.META['CSRF_COOKIE'] + if request.session.get(CSRF_SESSION_KEY) != request.META["CSRF_COOKIE"]: + request.session[CSRF_SESSION_KEY] = request.META["CSRF_COOKIE"] else: response.set_cookie( settings.CSRF_COOKIE_NAME, - request.META['CSRF_COOKIE'], + request.META["CSRF_COOKIE"], max_age=settings.CSRF_COOKIE_AGE, domain=settings.CSRF_COOKIE_DOMAIN, path=settings.CSRF_COOKIE_PATH, @@ -263,17 +272,17 @@ class CsrfViewMiddleware(MiddlewareMixin): samesite=settings.CSRF_COOKIE_SAMESITE, ) # Set the Vary header since content varies with the CSRF cookie. - patch_vary_headers(response, ('Cookie',)) + patch_vary_headers(response, ("Cookie",)) def _origin_verified(self, request): - request_origin = request.META['HTTP_ORIGIN'] + request_origin = request.META["HTTP_ORIGIN"] try: good_host = request.get_host() except DisallowedHost: pass else: - good_origin = '%s://%s' % ( - 'https' if request.is_secure() else 'http', + good_origin = "%s://%s" % ( + "https" if request.is_secure() else "http", good_host, ) if request_origin == good_origin: @@ -292,7 +301,7 @@ class CsrfViewMiddleware(MiddlewareMixin): ) def _check_referer(self, request): - referer = request.META.get('HTTP_REFERER') + referer = request.META.get("HTTP_REFERER") if referer is None: raise RejectRequest(REASON_NO_REFERER) @@ -302,11 +311,11 @@ class CsrfViewMiddleware(MiddlewareMixin): raise RejectRequest(REASON_MALFORMED_REFERER) # Make sure we have a valid URL for Referer. - if '' in (referer.scheme, referer.netloc): + if "" in (referer.scheme, referer.netloc): raise RejectRequest(REASON_MALFORMED_REFERER) # Ensure that our Referer is also secure. - if referer.scheme != 'https': + if referer.scheme != "https": raise RejectRequest(REASON_INSECURE_REFERER) if any( @@ -330,18 +339,18 @@ class CsrfViewMiddleware(MiddlewareMixin): raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) else: server_port = request.get_port() - if server_port not in ('443', '80'): - good_referer = '%s:%s' % (good_referer, server_port) + if server_port not in ("443", "80"): + good_referer = "%s:%s" % (good_referer, server_port) if not is_same_domain(referer.netloc, good_referer): raise RejectRequest(REASON_BAD_REFERER % referer.geturl()) def _bad_token_message(self, reason, token_source): - if token_source != 'POST': + if token_source != "POST": # Assume it is a settings.CSRF_HEADER_NAME value. header_name = HttpHeaders.parse_header_name(token_source) - token_source = f'the {header_name!r} HTTP header' - return f'CSRF token from {token_source} {reason}.' + token_source = f"the {header_name!r} HTTP header" + return f"CSRF token from {token_source} {reason}." def _check_token(self, request): # Access csrf_secret via self._get_secret() as rotate_token() may have @@ -350,7 +359,7 @@ class CsrfViewMiddleware(MiddlewareMixin): try: csrf_secret = self._get_secret(request) except InvalidTokenFormat as exc: - raise RejectRequest(f'CSRF cookie {exc.reason}.') + raise RejectRequest(f"CSRF cookie {exc.reason}.") if csrf_secret is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, @@ -359,10 +368,10 @@ class CsrfViewMiddleware(MiddlewareMixin): raise RejectRequest(REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. - request_csrf_token = '' - if request.method == 'POST': + request_csrf_token = "" + if request.method == "POST": try: - request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') + request_csrf_token = request.POST.get("csrfmiddlewaretoken", "") except UnreadablePostError: # Handle a broken connection before we've completed reading the # POST data. process_view shouldn't raise any exceptions, so @@ -370,7 +379,7 @@ class CsrfViewMiddleware(MiddlewareMixin): # listening, which they probably aren't because of the error). pass - if request_csrf_token == '': + if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, and # possible for PUT/DELETE. try: @@ -383,7 +392,7 @@ class CsrfViewMiddleware(MiddlewareMixin): raise RejectRequest(REASON_CSRF_TOKEN_MISSING) token_source = settings.CSRF_HEADER_NAME else: - token_source = 'POST' + token_source = "POST" try: _check_token_format(request_csrf_token) @@ -392,7 +401,7 @@ class CsrfViewMiddleware(MiddlewareMixin): raise RejectRequest(reason) if not _does_token_match(request_csrf_token, csrf_secret): - reason = self._bad_token_message('incorrect', token_source) + reason = self._bad_token_message("incorrect", token_source) raise RejectRequest(reason) def process_request(self, request): @@ -406,22 +415,22 @@ class CsrfViewMiddleware(MiddlewareMixin): # masked, this also causes it to be replaced with the unmasked # form, but only in cases where the secret is already getting # saved anyways. - request.META['CSRF_COOKIE'] = csrf_secret + request.META["CSRF_COOKIE"] = csrf_secret def process_view(self, request, callback, callback_args, callback_kwargs): - if getattr(request, 'csrf_processing_done', False): + if getattr(request, "csrf_processing_done", False): return None # Wait until request.META["CSRF_COOKIE"] has been manipulated before # bailing out, so that get_token still works - if getattr(callback, 'csrf_exempt', False): + if getattr(callback, "csrf_exempt", False): return None # Assume that anything not defined as 'safe' by RFC7231 needs protection - if request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): + if request.method in ("GET", "HEAD", "OPTIONS", "TRACE"): return self._accept(request) - if getattr(request, '_dont_enforce_csrf_checks', False): + 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 @@ -430,9 +439,11 @@ class CsrfViewMiddleware(MiddlewareMixin): # Reject the request if the Origin header doesn't match an allowed # value. - if 'HTTP_ORIGIN' in request.META: + if "HTTP_ORIGIN" in request.META: if not self._origin_verified(request): - return self._reject(request, REASON_BAD_ORIGIN % request.META['HTTP_ORIGIN']) + return self._reject( + request, REASON_BAD_ORIGIN % request.META["HTTP_ORIGIN"] + ) elif request.is_secure(): # If the Origin header wasn't provided, reject HTTPS requests if # the Referer header doesn't match an allowed value. @@ -464,7 +475,7 @@ class CsrfViewMiddleware(MiddlewareMixin): return self._accept(request) def process_response(self, request, response): - if request.META.get('CSRF_COOKIE_NEEDS_UPDATE'): + if request.META.get("CSRF_COOKIE_NEEDS_UPDATE"): self._set_csrf_cookie(request, response) # Unset the flag to prevent _set_csrf_cookie() from being # unnecessarily called again in process_response() by other @@ -473,6 +484,6 @@ class CsrfViewMiddleware(MiddlewareMixin): # CSRF_COOKIE_NEEDS_UPDATE is still respected in subsequent calls # e.g. in case rotate_token() is called in process_response() later # by custom middleware but before those subsequent calls. - request.META['CSRF_COOKIE_NEEDS_UPDATE'] = False + request.META["CSRF_COOKIE_NEEDS_UPDATE"] = False return response diff --git a/django/middleware/gzip.py b/django/middleware/gzip.py index 350466151d..6d27c1e335 100644 --- a/django/middleware/gzip.py +++ b/django/middleware/gzip.py @@ -3,7 +3,7 @@ from django.utils.deprecation import MiddlewareMixin from django.utils.regex_helper import _lazy_re_compile from django.utils.text import compress_sequence, compress_string -re_accepts_gzip = _lazy_re_compile(r'\bgzip\b') +re_accepts_gzip = _lazy_re_compile(r"\bgzip\b") class GZipMiddleware(MiddlewareMixin): @@ -12,18 +12,19 @@ class GZipMiddleware(MiddlewareMixin): Set the Vary header accordingly, so that caches will base their storage on the Accept-Encoding header. """ + def process_response(self, request, response): # It's not worth attempting to compress really short responses. if not response.streaming and len(response.content) < 200: return response # Avoid gzipping if we've already got a content-encoding. - if response.has_header('Content-Encoding'): + if response.has_header("Content-Encoding"): return response - patch_vary_headers(response, ('Accept-Encoding',)) + patch_vary_headers(response, ("Accept-Encoding",)) - ae = request.META.get('HTTP_ACCEPT_ENCODING', '') + ae = request.META.get("HTTP_ACCEPT_ENCODING", "") if not re_accepts_gzip.search(ae): return response @@ -31,21 +32,21 @@ class GZipMiddleware(MiddlewareMixin): # Delete the `Content-Length` header for streaming content, because # we won't know the compressed size until we stream it. response.streaming_content = compress_sequence(response.streaming_content) - del response.headers['Content-Length'] + del response.headers["Content-Length"] else: # Return the compressed content only if it's actually shorter. compressed_content = compress_string(response.content) if len(compressed_content) >= len(response.content): return response response.content = compressed_content - response.headers['Content-Length'] = str(len(response.content)) + response.headers["Content-Length"] = str(len(response.content)) # If there is a strong ETag, make it weak to fulfill the requirements # of RFC 7232 section-2.1 while also allowing conditional request # matches on ETags. - etag = response.get('ETag') + etag = response.get("ETag") if etag and etag.startswith('"'): - response.headers['ETag'] = 'W/' + etag - response.headers['Content-Encoding'] = 'gzip' + response.headers["ETag"] = "W/" + etag + response.headers["Content-Encoding"] = "gzip" return response diff --git a/django/middleware/http.py b/django/middleware/http.py index 4fcde85698..84c5466bb6 100644 --- a/django/middleware/http.py +++ b/django/middleware/http.py @@ -1,6 +1,4 @@ -from django.utils.cache import ( - cc_delim_re, get_conditional_response, set_response_etag, -) +from django.utils.cache import cc_delim_re, get_conditional_response, set_response_etag from django.utils.deprecation import MiddlewareMixin from django.utils.http import parse_http_date_safe @@ -11,18 +9,19 @@ class ConditionalGetMiddleware(MiddlewareMixin): Last-Modified header and the request has If-None-Match or If-Modified-Since, replace the response with HttpNotModified. Add an ETag header if needed. """ + def process_response(self, request, response): # It's too late to prevent an unsafe request with a 412 response, and # for a HEAD request, the response body is always empty so computing # an accurate ETag isn't possible. - if request.method != 'GET': + if request.method != "GET": return response - if self.needs_etag(response) and not response.has_header('ETag'): + if self.needs_etag(response) and not response.has_header("ETag"): set_response_etag(response) - etag = response.get('ETag') - last_modified = response.get('Last-Modified') + etag = response.get("ETag") + last_modified = response.get("Last-Modified") last_modified = last_modified and parse_http_date_safe(last_modified) if etag or last_modified: @@ -37,5 +36,5 @@ class ConditionalGetMiddleware(MiddlewareMixin): def needs_etag(self, response): """Return True if an ETag header should be added to response.""" - cache_control_headers = cc_delim_re.split(response.get('Cache-Control', '')) - return all(header.lower() != 'no-store' for header in cache_control_headers) + cache_control_headers = cc_delim_re.split(response.get("Cache-Control", "")) + return all(header.lower() != "no-store" for header in cache_control_headers) diff --git a/django/middleware/locale.py b/django/middleware/locale.py index d90fc84152..71db230da2 100644 --- a/django/middleware/locale.py +++ b/django/middleware/locale.py @@ -13,14 +13,24 @@ class LocaleMiddleware(MiddlewareMixin): current thread context. This allows pages to be dynamically translated to the language the user desires (if the language is available). """ + response_redirect_class = HttpResponseRedirect def process_request(self, request): - urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) - i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) - language = translation.get_language_from_request(request, check_path=i18n_patterns_used) + urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) + ( + i18n_patterns_used, + prefixed_default_language, + ) = is_language_prefix_patterns_used(urlconf) + language = translation.get_language_from_request( + request, check_path=i18n_patterns_used + ) language_from_path = translation.get_language_from_path(request.path_info) - if not language_from_path and i18n_patterns_used and not prefixed_default_language: + if ( + not language_from_path + and i18n_patterns_used + and not prefixed_default_language + ): language = settings.LANGUAGE_CODE translation.activate(language) request.LANGUAGE_CODE = translation.get_language() @@ -28,39 +38,43 @@ class LocaleMiddleware(MiddlewareMixin): def process_response(self, request, response): language = translation.get_language() language_from_path = translation.get_language_from_path(request.path_info) - urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) - i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) + urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF) + ( + i18n_patterns_used, + prefixed_default_language, + ) = is_language_prefix_patterns_used(urlconf) - if (response.status_code == 404 and not language_from_path and - i18n_patterns_used and prefixed_default_language): + if ( + response.status_code == 404 + and not language_from_path + and i18n_patterns_used + and prefixed_default_language + ): # Maybe the language code is missing in the URL? Try adding the # language prefix and redirecting to that URL. - language_path = '/%s%s' % (language, request.path_info) + language_path = "/%s%s" % (language, request.path_info) path_valid = is_valid_path(language_path, urlconf) - path_needs_slash = ( - not path_valid and ( - settings.APPEND_SLASH and not language_path.endswith('/') and - is_valid_path('%s/' % language_path, urlconf) - ) + path_needs_slash = not path_valid and ( + settings.APPEND_SLASH + and not language_path.endswith("/") + and is_valid_path("%s/" % language_path, urlconf) ) if path_valid or path_needs_slash: script_prefix = get_script_prefix() # Insert language after the script prefix and before the # rest of the URL - language_url = request.get_full_path(force_append_slash=path_needs_slash).replace( - script_prefix, - '%s%s/' % (script_prefix, language), - 1 - ) + language_url = request.get_full_path( + force_append_slash=path_needs_slash + ).replace(script_prefix, "%s%s/" % (script_prefix, language), 1) # Redirect to the language-specific URL as detected by # get_language_from_request(). HTTP caches may cache this # redirect, so add the Vary header. redirect = self.response_redirect_class(language_url) - patch_vary_headers(redirect, ('Accept-Language', 'Cookie')) + patch_vary_headers(redirect, ("Accept-Language", "Cookie")) return redirect if not (i18n_patterns_used and language_from_path): - patch_vary_headers(response, ('Accept-Language',)) - response.headers.setdefault('Content-Language', language) + patch_vary_headers(response, ("Accept-Language",)) + response.headers.setdefault("Content-Language", language) return response diff --git a/django/middleware/security.py b/django/middleware/security.py index d2c2bf2d3f..1dd2204814 100644 --- a/django/middleware/security.py +++ b/django/middleware/security.py @@ -20,38 +20,47 @@ class SecurityMiddleware(MiddlewareMixin): def process_request(self, request): path = request.path.lstrip("/") - if (self.redirect and not request.is_secure() and - not any(pattern.search(path) - for pattern in self.redirect_exempt)): + if ( + self.redirect + and not request.is_secure() + and not any(pattern.search(path) for pattern in self.redirect_exempt) + ): host = self.redirect_host or request.get_host() return HttpResponsePermanentRedirect( "https://%s%s" % (host, request.get_full_path()) ) def process_response(self, request, response): - if (self.sts_seconds and request.is_secure() and - 'Strict-Transport-Security' not in response): + if ( + self.sts_seconds + and request.is_secure() + and "Strict-Transport-Security" not in response + ): sts_header = "max-age=%s" % self.sts_seconds if self.sts_include_subdomains: sts_header = sts_header + "; includeSubDomains" if self.sts_preload: sts_header = sts_header + "; preload" - response.headers['Strict-Transport-Security'] = sts_header + response.headers["Strict-Transport-Security"] = sts_header if self.content_type_nosniff: - response.headers.setdefault('X-Content-Type-Options', 'nosniff') + response.headers.setdefault("X-Content-Type-Options", "nosniff") if self.referrer_policy: # Support a comma-separated string or iterable of values to allow # fallback. - response.headers.setdefault('Referrer-Policy', ','.join( - [v.strip() for v in self.referrer_policy.split(',')] - if isinstance(self.referrer_policy, str) else self.referrer_policy - )) + response.headers.setdefault( + "Referrer-Policy", + ",".join( + [v.strip() for v in self.referrer_policy.split(",")] + if isinstance(self.referrer_policy, str) + else self.referrer_policy + ), + ) if self.cross_origin_opener_policy: response.setdefault( - 'Cross-Origin-Opener-Policy', + "Cross-Origin-Opener-Policy", self.cross_origin_opener_policy, ) return response |
