[Python-modules-commits] [python-django] 01/03: Imported Upstream version 1.7.3

Luke Faraone lfaraone at moszumanska.debian.org
Fri Jan 16 05:44:51 UTC 2015


This is an automated email from the git hooks/post-receive script.

lfaraone pushed a commit to branch debian/experimental
in repository python-django.

commit 964a50e14d6553f866d750dc26104c81be06f4a7
Author: Luke Faraone <lfaraone at debian.org>
Date:   Thu Jan 15 21:40:52 2015 -0800

    Imported Upstream version 1.7.3
---
 Django.egg-info/PKG-INFO                  |  2 +-
 Django.egg-info/SOURCES.txt               |  5 ++
 PKG-INFO                                  |  2 +-
 django/__init__.py                        |  2 +-
 django/conf/locale/el/formats.py          | 36 ++++++++----
 django/contrib/auth/decorators.py         |  5 +-
 django/contrib/auth/hashers.py            |  4 +-
 django/contrib/auth/tests/test_hashers.py |  6 +-
 django/contrib/auth/tests/test_views.py   | 26 ++++++++-
 django/core/servers/basehttp.py           | 11 ++++
 django/db/migrations/migration.py         | 40 +++++++++-----
 django/forms/models.py                    | 28 ++++++++--
 django/middleware/csrf.py                 |  6 +-
 django/shortcuts.py                       |  7 +++
 django/utils/http.py                      |  1 +
 django/views/static.py                    |  5 +-
 docs/howto/auth-remote-user.txt           | 16 ++++++
 docs/index.txt                            | 12 ++--
 docs/ref/contrib/admin/index.txt          |  2 +-
 docs/ref/schema-editor.txt                | 17 ++++++
 docs/releases/1.4.18.txt                  | 68 +++++++++++++++++++++++
 docs/releases/1.6.10.txt                  | 69 +++++++++++++++++++++++
 docs/releases/1.7.3.txt                   | 91 +++++++++++++++++++++++++++++++
 docs/releases/index.txt                   |  3 +
 docs/spelling_wordlist                    |  1 +
 docs/topics/forms/index.txt               |  4 +-
 docs/topics/migrations.txt                | 67 +++++++++++++++++++++++
 tests/csrf_tests/tests.py                 |  5 ++
 tests/logging_tests/tests.py              | 16 ++++--
 tests/migrations/test_operations.py       | 28 ++++++----
 tests/model_forms/tests.py                | 21 +++++++
 tests/resolve_url/tests.py                | 12 +++-
 tests/resolve_url/urls.py                 |  5 +-
 tests/runtests.py                         |  5 ++
 tests/servers/test_basehttp.py            | 67 +++++++++++++++++++++++
 tests/test_runner/tests.py                |  2 +-
 tests/utils_tests/test_http.py            |  3 +-
 tests/view_tests/media/long-line.txt      |  1 +
 tests/view_tests/tests/test_static.py     | 10 +++-
 39 files changed, 632 insertions(+), 79 deletions(-)

diff --git a/Django.egg-info/PKG-INFO b/Django.egg-info/PKG-INFO
index 83b4829..acf7c27 100644
--- a/Django.egg-info/PKG-INFO
+++ b/Django.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Django
-Version: 1.7.2
+Version: 1.7.3
 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
 Home-page: http://www.djangoproject.com/
 Author: Django Software Foundation
diff --git a/Django.egg-info/SOURCES.txt b/Django.egg-info/SOURCES.txt
index e629edf..92cd27d 100644
--- a/Django.egg-info/SOURCES.txt
+++ b/Django.egg-info/SOURCES.txt
@@ -3981,6 +3981,7 @@ docs/releases/1.4.14.txt
 docs/releases/1.4.15.txt
 docs/releases/1.4.16.txt
 docs/releases/1.4.17.txt
+docs/releases/1.4.18.txt
 docs/releases/1.4.2.txt
 docs/releases/1.4.3.txt
 docs/releases/1.4.4.txt
@@ -4004,6 +4005,7 @@ docs/releases/1.5.8.txt
 docs/releases/1.5.9.txt
 docs/releases/1.5.txt
 docs/releases/1.6.1.txt
+docs/releases/1.6.10.txt
 docs/releases/1.6.2.txt
 docs/releases/1.6.3.txt
 docs/releases/1.6.4.txt
@@ -4015,6 +4017,7 @@ docs/releases/1.6.9.txt
 docs/releases/1.6.txt
 docs/releases/1.7.1.txt
 docs/releases/1.7.2.txt
+docs/releases/1.7.3.txt
 docs/releases/1.7.txt
 docs/releases/index.txt
 docs/releases/security.txt
@@ -5005,6 +5008,7 @@ tests/serializers_regress/models.py
 tests/serializers_regress/tests.py
 tests/servers/__init__.py
 tests/servers/models.py
+tests/servers/test_basehttp.py
 tests/servers/tests.py
 tests/servers/urls.py
 tests/servers/views.py
@@ -5363,6 +5367,7 @@ tests/view_tests/locale/ru/LC_MESSAGES/djangojs.po
 tests/view_tests/media/file.txt
 tests/view_tests/media/file.txt.gz
 tests/view_tests/media/file.unknown
+tests/view_tests/media/long-line.txt
 tests/view_tests/other_templates/render_dirs_test.html
 tests/view_tests/templates/jsi18n.html
 tests/view_tests/templates/debug/render_test.html
diff --git a/PKG-INFO b/PKG-INFO
index 83b4829..acf7c27 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Django
-Version: 1.7.2
+Version: 1.7.3
 Summary: A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
 Home-page: http://www.djangoproject.com/
 Author: Django Software Foundation
diff --git a/django/__init__.py b/django/__init__.py
index 5df6a56..424dc1f 100644
--- a/django/__init__.py
+++ b/django/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (1, 7, 2, 'final', 0)
+VERSION = (1, 7, 3, 'final', 0)
 
 
 def get_version(*args, **kwargs):
diff --git a/django/conf/locale/el/formats.py b/django/conf/locale/el/formats.py
index 817f71d..429db4b 100644
--- a/django/conf/locale/el/formats.py
+++ b/django/conf/locale/el/formats.py
@@ -5,20 +5,34 @@ from __future__ import unicode_literals
 
 # The *_FORMAT strings use the Django date format syntax,
 # see http://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
-DATE_FORMAT = 'd E Y'
-TIME_FORMAT = 'g:i:s A'
-# DATETIME_FORMAT =
+DATE_FORMAT = 'd/m/Y'
+TIME_FORMAT = 'P'
+DATETIME_FORMAT = 'd/m/Y P'
 YEAR_MONTH_FORMAT = 'F Y'
 MONTH_DAY_FORMAT = 'j F'
-SHORT_DATE_FORMAT = 'd M Y'
-# SHORT_DATETIME_FORMAT =
-# FIRST_DAY_OF_WEEK =
+SHORT_DATE_FORMAT = 'd/m/Y'
+SHORT_DATETIME_FORMAT = 'd/m/Y P'
+FIRST_DAY_OF_WEEK = 0  # Sunday
 
 # The *_INPUT_FORMATS strings use the Python strftime format syntax,
 # see http://docs.python.org/library/datetime.html#strftime-strptime-behavior
-# DATE_INPUT_FORMATS =
-# TIME_INPUT_FORMATS =
-# DATETIME_INPUT_FORMATS =
+DATE_INPUT_FORMATS = (
+    '%d/%m/%Y', '%d/%m/%y', '%Y-%m-%d',  # '25/10/2006', '25/10/06', '2006-10-25',
+)
+DATETIME_INPUT_FORMATS = (
+    '%d/%m/%Y %H:%M:%S',     # '25/10/2006 14:30:59'
+    '%d/%m/%Y %H:%M:%S.%f',  # '25/10/2006 14:30:59.000200'
+    '%d/%m/%Y %H:%M',        # '25/10/2006 14:30'
+    '%d/%m/%Y',              # '25/10/2006'
+    '%d/%m/%y %H:%M:%S',     # '25/10/06 14:30:59'
+    '%d/%m/%y %H:%M:%S.%f',  # '25/10/06 14:30:59.000200'
+    '%d/%m/%y %H:%M',        # '25/10/06 14:30'
+    '%d/%m/%y',              # '25/10/06'
+    '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59'
+    '%Y-%m-%d %H:%M:%S.%f',  # '2006-10-25 14:30:59.000200'
+    '%Y-%m-%d %H:%M',        # '2006-10-25 14:30'
+    '%Y-%m-%d',              # '2006-10-25'
+)
 DECIMAL_SEPARATOR = ','
-THOUSAND_SEPARATOR = '.'
-# NUMBER_GROUPING =
+THOUSAND_SEPARATOR = '\xa0'  # non-breaking space
+NUMBER_GROUPING = 3
diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py
index 12a79c0..99e2983 100644
--- a/django/contrib/auth/decorators.py
+++ b/django/contrib/auth/decorators.py
@@ -3,7 +3,6 @@ from django.conf import settings
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.core.exceptions import PermissionDenied
 from django.utils.decorators import available_attrs
-from django.utils.encoding import force_str
 from django.utils.six.moves.urllib.parse import urlparse
 from django.shortcuts import resolve_url
 
@@ -21,9 +20,7 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
             if test_func(request.user):
                 return view_func(request, *args, **kwargs)
             path = request.build_absolute_uri()
-            # urlparse chokes on lazy objects in Python 3, force to str
-            resolved_login_url = force_str(
-                resolve_url(login_url or settings.LOGIN_URL))
+            resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
             # If the login url is the same scheme and net location then just
             # use the path as the "next" url.
             login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
diff --git a/django/contrib/auth/hashers.py b/django/contrib/auth/hashers.py
index 4e40a7c..e459f39 100644
--- a/django/contrib/auth/hashers.py
+++ b/django/contrib/auth/hashers.py
@@ -222,12 +222,12 @@ class PBKDF2PasswordHasher(BasePasswordHasher):
     """
     Secure password hashing using the PBKDF2 algorithm (recommended)
 
-    Configured to use PBKDF2 + HMAC + SHA256 with 12000 iterations.
+    Configured to use PBKDF2 + HMAC + SHA256 with 15000 iterations.
     The result is a 64 byte binary string.  Iterations may be changed
     safely but you must rename the algorithm if you change SHA256.
     """
     algorithm = "pbkdf2_sha256"
-    iterations = 12000
+    iterations = 15000
     digest = hashlib.sha256
 
     def encode(self, password, salt, iterations=None):
diff --git a/django/contrib/auth/tests/test_hashers.py b/django/contrib/auth/tests/test_hashers.py
index 58628cd..85f1c15 100644
--- a/django/contrib/auth/tests/test_hashers.py
+++ b/django/contrib/auth/tests/test_hashers.py
@@ -47,7 +47,7 @@ class TestUtilsHashPass(SimpleTestCase):
     def test_pkbdf2(self):
         encoded = make_password('lètmein', 'seasalt', 'pbkdf2_sha256')
         self.assertEqual(encoded,
-            'pbkdf2_sha256$12000$seasalt$Ybw8zsFxqja97tY/o6G+Fy1ksY4U/Hw3DRrGED6Up4s=')
+            'pbkdf2_sha256$15000$seasalt$+qoFTwR4r71UCLMhmQUCou/LMu17XwQWfYIVd/xJ1RI=')
         self.assertTrue(is_password_usable(encoded))
         self.assertTrue(check_password('lètmein', encoded))
         self.assertFalse(check_password('lètmeinz', encoded))
@@ -211,14 +211,14 @@ class TestUtilsHashPass(SimpleTestCase):
         hasher = PBKDF2PasswordHasher()
         encoded = hasher.encode('lètmein', 'seasalt2')
         self.assertEqual(encoded,
-            'pbkdf2_sha256$12000$seasalt2$hlDLKsxgkgb1aeOppkM5atCYw5rPzAjCNQZ4NYyUROw=')
+            'pbkdf2_sha256$15000$seasalt2$uSQqI+91wgObKdP6L6S75LLzyxrZRWNcaujEZPA3/nA=')
         self.assertTrue(hasher.verify('lètmein', encoded))
 
     def test_low_level_pbkdf2_sha1(self):
         hasher = PBKDF2SHA1PasswordHasher()
         encoded = hasher.encode('lètmein', 'seasalt2')
         self.assertEqual(encoded,
-            'pbkdf2_sha1$12000$seasalt2$JeMRVfjjgtWw3/HzlnlfqBnQ6CA=')
+            'pbkdf2_sha1$15000$seasalt2$iYDXAPKgMsKMsarvA1MErD518Ug=')
         self.assertTrue(hasher.verify('lètmein', encoded))
 
     def test_upgrade(self):
diff --git a/django/contrib/auth/tests/test_views.py b/django/contrib/auth/tests/test_views.py
index 1a73af5..d9fbd0b 100644
--- a/django/contrib/auth/tests/test_views.py
+++ b/django/contrib/auth/tests/test_views.py
@@ -1,3 +1,6 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
 from importlib import import_module
 import itertools
 import os
@@ -9,7 +12,7 @@ from django.contrib.sites.requests import RequestSite
 from django.contrib.admin.models import LogEntry
 from django.contrib.auth.models import User
 from django.core import mail
-from django.core.urlresolvers import reverse, NoReverseMatch
+from django.core.urlresolvers import NoReverseMatch, reverse, reverse_lazy
 from django.http import QueryDict, HttpRequest
 from django.utils.encoding import force_text
 from django.utils.http import urlquote
@@ -27,7 +30,7 @@ from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
 # Needed so model is installed when tests are run independently:
 from django.contrib.auth.tests.custom_user import CustomUser  # NOQA
 from django.contrib.auth.tests.utils import skipIfCustomUser
-from django.contrib.auth.views import login as login_view
+from django.contrib.auth.views import login as login_view, redirect_to_login
 
 
 @override_settings(
@@ -652,6 +655,10 @@ class LoginURLSettings(AuthViewsTestCase):
         expected = 'http://remote.example.com/login/?next=%s' % quoted_next
         self.assertLoginURLEquals(expected)
 
+    @override_settings(LOGIN_URL=reverse_lazy('login'))
+    def test_lazy_login_url(self):
+        self.assertLoginURLEquals('/login/?next=/login_required/')
+
 
 @skipIfCustomUser
 class LoginRedirectUrlTest(AuthViewsTestCase):
@@ -677,6 +684,21 @@ class LoginRedirectUrlTest(AuthViewsTestCase):
         self.assertLoginRedirectURLEqual('http://remote.example.com/welcome/')
 
 
+class RedirectToLoginTests(AuthViewsTestCase):
+    """Tests for the redirect_to_login view"""
+    @override_settings(LOGIN_URL=reverse_lazy('login'))
+    def test_redirect_to_login_with_lazy(self):
+        login_redirect_response = redirect_to_login(next='/else/where/')
+        expected = '/login/?next=/else/where/'
+        self.assertEqual(expected, login_redirect_response.url)
+
+    @override_settings(LOGIN_URL=reverse_lazy('login'))
+    def test_redirect_to_login_with_lazy_and_unicode(self):
+        login_redirect_response = redirect_to_login(next='/else/where/झ/')
+        expected = '/login/?next=/else/where/%E0%A4%9D/'
+        self.assertEqual(expected, login_redirect_response.url)
+
+
 @skipIfCustomUser
 class LogoutTest(AuthViewsTestCase):
 
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py
index 090a694..cc1ff9b 100644
--- a/django/core/servers/basehttp.py
+++ b/django/core/servers/basehttp.py
@@ -155,6 +155,17 @@ class WSGIRequestHandler(simple_server.WSGIRequestHandler, object):
 
         sys.stderr.write(msg)
 
+    def get_environ(self):
+        # Strip all headers with underscores in the name before constructing
+        # the WSGI environ. This prevents header-spoofing based on ambiguity
+        # between underscores and dashes both normalized to underscores in WSGI
+        # env vars. Nginx and Apache 2.4+ both do this as well.
+        for k, v in self.headers.items():
+            if '_' in k:
+                del self.headers[k]
+
+        return super(WSGIRequestHandler, self).get_environ()
+
 
 def run(addr, port, wsgi_handler, ipv6=False, threading=False):
     server_address = (addr, port)
diff --git a/django/db/migrations/migration.py b/django/db/migrations/migration.py
index 80bdb2f..79f6bfb 100644
--- a/django/db/migrations/migration.py
+++ b/django/db/migrations/migration.py
@@ -115,29 +115,39 @@ class Migration(object):
         Takes a project_state representing all migrations prior to this one
         and a schema_editor for a live database and applies the migration
         in a reverse order.
+
+        The backwards migration process consists of two phases:
+
+        1. The intermediate states from right before the first until right
+           after the last operation inside this migration are preserved.
+        2. The operations are applied in reverse order using the states
+           recorded in step 1.
         """
-        # We need to pre-calculate the stack of project states
+        # Construct all the intermediate states we need for a reverse migration
         to_run = []
+        new_state = project_state
+        # Phase 1
         for operation in self.operations:
-            # If this operation cannot be represented as SQL, place a comment
-            # there instead
-            if collect_sql and not operation.reduces_to_sql:
-                schema_editor.collected_sql.append("--")
-                schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE "
-                                                   "WRITTEN AS SQL:")
-                schema_editor.collected_sql.append("-- %s" % operation.describe())
-                schema_editor.collected_sql.append("--")
-                continue
             # If it's irreversible, error out
             if not operation.reversible:
                 raise Migration.IrreversibleError("Operation %s in %s is not reversible" % (operation, self))
-            new_state = project_state.clone()
+            # Preserve new state from previous run to not tamper the same state
+            # over all operations
+            new_state = new_state.clone()
+            old_state = new_state.clone()
             operation.state_forwards(self.app_label, new_state)
-            to_run.append((operation, project_state, new_state))
-            project_state = new_state
-        # Now run them in reverse
-        to_run.reverse()
+            to_run.insert(0, (operation, old_state, new_state))
+
+        # Phase 2
         for operation, to_state, from_state in to_run:
+            if collect_sql:
+                if not operation.reduces_to_sql:
+                    schema_editor.collected_sql.append("--")
+                    schema_editor.collected_sql.append("-- MIGRATION NOW PERFORMS OPERATION THAT CANNOT BE "
+                                                       "WRITTEN AS SQL:")
+                    schema_editor.collected_sql.append("-- %s" % operation.describe())
+                    schema_editor.collected_sql.append("--")
+                    continue
             if not schema_editor.connection.features.can_rollback_ddl and operation.atomic:
                 # We're forcing a transaction on a non-transactional-DDL backend
                 with atomic(schema_editor.connection.alias):
diff --git a/django/forms/models.py b/django/forms/models.py
index 0f1f54f..37ef32d 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -1221,8 +1221,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
     def to_python(self, value):
         if not value:
             return []
-        to_py = super(ModelMultipleChoiceField, self).to_python
-        return [to_py(val) for val in value]
+        return list(self._check_values(value))
 
     def clean(self, value):
         if self.required and not value:
@@ -1231,7 +1230,29 @@ class ModelMultipleChoiceField(ModelChoiceField):
             return self.queryset.none()
         if not isinstance(value, (list, tuple)):
             raise ValidationError(self.error_messages['list'], code='list')
+        qs = self._check_values(value)
+        # Since this overrides the inherited ModelChoiceField.clean
+        # we run custom validators here
+        self.run_validators(value)
+        return qs
+
+    def _check_values(self, value):
+        """
+        Given a list of possible PK values, returns a QuerySet of the
+        corresponding objects. Raises a ValidationError if a given value is
+        invalid (not a valid PK, not in the queryset, etc.)
+        """
         key = self.to_field_name or 'pk'
+        # deduplicate given values to avoid creating many querysets or
+        # requiring the database backend deduplicate efficiently.
+        try:
+            value = frozenset(value)
+        except TypeError:
+            # list of lists isn't hashable, for example
+            raise ValidationError(
+                self.error_messages['list'],
+                code='list',
+            )
         for pk in value:
             try:
                 self.queryset.filter(**{key: pk})
@@ -1250,9 +1271,6 @@ class ModelMultipleChoiceField(ModelChoiceField):
                     code='invalid_choice',
                     params={'value': val},
                 )
-        # Since this overrides the inherited ModelChoiceField.clean
-        # we run custom validators here
-        self.run_validators(value)
         return qs
 
     def prepare_value(self, value):
diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py
index eb7fe7d..dfeefa4 100644
--- a/django/middleware/csrf.py
+++ b/django/middleware/csrf.py
@@ -148,7 +148,11 @@ class CsrfViewMiddleware(object):
                 # Barth et al. found that the Referer header is missing for
                 # same-domain requests in only about 0.2% of cases or less, so
                 # we can use strict Referer checking.
-                referer = request.META.get('HTTP_REFERER')
+                referer = force_text(
+                    request.META.get('HTTP_REFERER'),
+                    strings_only=True,
+                    errors='replace'
+                )
                 if referer is None:
                     return self._reject(request, REASON_NO_REFERER)
 
diff --git a/django/shortcuts.py b/django/shortcuts.py
index 6256047..d995813 100644
--- a/django/shortcuts.py
+++ b/django/shortcuts.py
@@ -11,6 +11,8 @@ from django.db.models.manager import Manager
 from django.db.models.query import QuerySet
 from django.core import urlresolvers
 from django.utils import six
+from django.utils.encoding import force_text
+from django.utils.functional import Promise
 
 
 def render_to_response(*args, **kwargs):
@@ -148,6 +150,11 @@ def resolve_url(to, *args, **kwargs):
     if hasattr(to, 'get_absolute_url'):
         return to.get_absolute_url()
 
+    if isinstance(to, Promise):
+        # Expand the lazy instance, as it can cause issues when it is passed
+        # further to some Python functions like urlparse.
+        to = force_text(to)
+
     if isinstance(to, six.string_types):
         # Handle relative URLs
         if any(to.startswith(path) for path in ('./', '../')):
diff --git a/django/utils/http.py b/django/utils/http.py
index 67414d9..6aa5cd3 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -272,6 +272,7 @@ def is_safe_url(url, host=None):
     """
     if not url:
         return False
+    url = url.strip()
     # Chrome treats \ completely as /
     url = url.replace('\\', '/')
     # Chrome considers any URL with more than two slashes to be absolute, but
diff --git a/django/views/static.py b/django/views/static.py
index 68fb7c4..0ce00a9 100644
--- a/django/views/static.py
+++ b/django/views/static.py
@@ -17,6 +17,8 @@ from django.utils.http import http_date, parse_http_date
 from django.utils.six.moves.urllib.parse import unquote
 from django.utils.translation import ugettext as _, ugettext_lazy
 
+STREAM_CHUNK_SIZE = 4096
+
 
 def serve(request, path, document_root=None, show_indexes=False):
     """
@@ -61,7 +63,8 @@ def serve(request, path, document_root=None, show_indexes=False):
         return HttpResponseNotModified()
     content_type, encoding = mimetypes.guess_type(fullpath)
     content_type = content_type or 'application/octet-stream'
-    response = StreamingHttpResponse(open(fullpath, 'rb'),
+    f = open(fullpath, 'rb')
+    response = StreamingHttpResponse(iter(lambda: f.read(STREAM_CHUNK_SIZE), b''),
                                      content_type=content_type)
     response["Last-Modified"] = http_date(statobj.st_mtime)
     if stat.S_ISREG(statobj.st_mode):
diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt
index 2edab6b..dc96a98 100644
--- a/docs/howto/auth-remote-user.txt
+++ b/docs/howto/auth-remote-user.txt
@@ -64,6 +64,22 @@ If your authentication mechanism uses a custom HTTP header and not
     class CustomHeaderMiddleware(RemoteUserMiddleware):
         header = 'HTTP_AUTHUSER'
 
+.. warning::
+
+    Be very careful if using a ``RemoteUserMiddleware`` subclass with a custom
+    HTTP header. You must be sure that your front-end web server always sets or
+    strips that header based on the appropriate authentication checks, never
+    permitting an end-user to submit a fake (or "spoofed") header value. Since
+    the HTTP headers ``X-Auth-User`` and ``X-Auth_User`` (for example) both
+    normalize to the ``HTTP_X_AUTH_USER`` key in ``request.META``, you must
+    also check that your web server doesn't allow a spoofed header using
+    underscores in place of dashes.
+
+    This warning doesn't apply to ``RemoteUserMiddleware`` in its default
+    configuration with ``header = 'REMOTE_USER'``, since a key that doesn't
+    start with ``HTTP_`` in ``request.META`` can only be set by your WSGI
+    server, not directly from an HTTP request header.
+
 If you need more control, you can create your own authentication backend
 that inherits from :class:`~django.contrib.auth.backends.RemoteUserBackend` and
 override one or more of its attributes and methods.
diff --git a/docs/index.txt b/docs/index.txt
index 959b655..f0744d2 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -41,12 +41,12 @@ Are you new to Django or to programming? This is the place to start!
   :doc:`Installation <intro/install>`
 
 * **Tutorial:**
-  :doc:`Part 1 <intro/tutorial01>` |
-  :doc:`Part 2 <intro/tutorial02>` |
-  :doc:`Part 3 <intro/tutorial03>` |
-  :doc:`Part 4 <intro/tutorial04>` |
-  :doc:`Part 5 <intro/tutorial05>` |
-  :doc:`Part 6 <intro/tutorial06>`
+  :doc:`Part 1: Models <intro/tutorial01>` |
+  :doc:`Part 2: The admin site <intro/tutorial02>` |
+  :doc:`Part 3: Views and templates <intro/tutorial03>` |
+  :doc:`Part 4: Forms and generic views <intro/tutorial04>` |
+  :doc:`Part 5: Testing <intro/tutorial05>` |
+  :doc:`Part 6: Static files <intro/tutorial06>`
 
 * **Advanced Tutorials:**
   :doc:`How to write reusable apps <intro/reusable-apps>` |
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index e25557a..ea00cb3 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -681,7 +681,7 @@ subclass::
 
       For example::
 
-          class Person(object):
+          class Person(models.Model):
               first_name = models.CharField(max_length=50)
               last_name = models.CharField(max_length=50)
 
diff --git a/docs/ref/schema-editor.txt b/docs/ref/schema-editor.txt
index e4d3fa0..94456d0 100644
--- a/docs/ref/schema-editor.txt
+++ b/docs/ref/schema-editor.txt
@@ -149,3 +149,20 @@ If the database has the ``supports_combined_alters``, Django will try and
 do as many of these in a single database call as possible; otherwise, it will
 issue a separate ALTER statement for each change, but will not issue ALTERs
 where no change is required (as South often did).
+
+Attributes
+==========
+
+All attributes should be considered read-only unless stated otherwise.
+
+connection
+----------
+
+.. attribute:: SchemaEditor.connection
+
+A connection object to the database. A useful attribute of the connection is
+``alias`` which can be used to determine the name of the database being
+accessed.
+
+This is useful when doing data migrations for :ref:`migrations with multiple
+databases <data-migrations-and-multiple-databases>`.
diff --git a/docs/releases/1.4.18.txt b/docs/releases/1.4.18.txt
new file mode 100644
index 0000000..b154d87
--- /dev/null
+++ b/docs/releases/1.4.18.txt
@@ -0,0 +1,68 @@
+===========================
+Django 1.4.18 release notes
+===========================
+
+*January 13, 2015*
+
+Django 1.4.18 fixes several security issues in 1.4.17 as well as a regression
+on Python 2.5 in the 1.4.17 release.
+
+WSGI header spoofing via underscore/dash conflation
+===================================================
+
+When HTTP headers are placed into the WSGI environ, they are normalized by
+converting to uppercase, converting all dashes to underscores, and prepending
+`HTTP_`. For instance, a header ``X-Auth-User`` would become
+``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
+``request.META`` dictionary).
+
+Unfortunately, this means that the WSGI environ cannot distinguish between
+headers containing dashes and headers containing underscores: ``X-Auth-User``
+and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
+header is used in a security-sensitive way (for instance, passing
+authentication information along from a front-end proxy), even if the proxy
+carefully strips any incoming value for ``X-Auth-User``, an attacker may be
+able to provide an ``X-Auth_User`` header (with underscore) and bypass this
+protection.
+
+In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
+containing underscores from incoming requests by default. Django's built-in
+development server now does the same. Django's development server is not
+recommended for production use, but matching the behavior of common production
+servers reduces the surface area for behavior changes during deployment.
+
+Mitigated possible XSS attack via user-supplied redirect URLs
+=============================================================
+
+Django relies on user input in some cases (e.g.
+:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+to redirect the user to an "on success" URL. The security checks for these
+redirects (namely ``django.util.http.is_safe_url()``) didn't strip leading
+whitespace on the tested URL and as such considered URLs like
+``\njavascript:...`` safe. If a developer relied on ``is_safe_url()`` to
+provide safe redirect targets and put such a URL into a link, they could suffer
+from a XSS attack. This bug doesn't affect Django currently, since we only put
+this URL into the ``Location`` response header and browsers seem to ignore
+JavaScript there.
+
+Denial-of-service attack against ``django.views.static.serve``
+==============================================================
+
+In older versions of Django, the :func:`django.views.static.serve` view read
+the files it served one line at a time. Therefore, a big file with no newlines
+would result in memory usage equal to the size of that file. An attacker could
+exploit this and launch a denial-of-service attack by simultaneously requesting
+many large files. This view now reads the file in chunks to prevent large
+memory usage.
+
+Note, however, that this view has always carried a warning that it is not
+hardened for production use and should be used only as a development aid. Now
+may be a good time to audit your project and serve your files in production
+using a real front-end web server if you are not doing so.
+
+Bugfixes
+========
+
+* To maintain compatibility with Python 2.5, Django's vendored version of six,
+  :mod:`django.utils.six`, has been downgraded to 1.8.0 which is the last
+  version to support Python 2.5.
diff --git a/docs/releases/1.6.10.txt b/docs/releases/1.6.10.txt
new file mode 100644
index 0000000..a559bfc
--- /dev/null
+++ b/docs/releases/1.6.10.txt
@@ -0,0 +1,69 @@
+===========================
+Django 1.6.10 release notes
+===========================
+
+*January 13, 2015*
+
+Django 1.6.10 fixes several security issues in 1.6.9.
+
+WSGI header spoofing via underscore/dash conflation
+===================================================
+
+When HTTP headers are placed into the WSGI environ, they are normalized by
+converting to uppercase, converting all dashes to underscores, and prepending
+`HTTP_`. For instance, a header ``X-Auth-User`` would become
+``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
+``request.META`` dictionary).
+
+Unfortunately, this means that the WSGI environ cannot distinguish between
+headers containing dashes and headers containing underscores: ``X-Auth-User``
+and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
+header is used in a security-sensitive way (for instance, passing
+authentication information along from a front-end proxy), even if the proxy
+carefully strips any incoming value for ``X-Auth-User``, an attacker may be
+able to provide an ``X-Auth_User`` header (with underscore) and bypass this
+protection.
+
+In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
+containing underscores from incoming requests by default. Django's built-in
+development server now does the same. Django's development server is not
+recommended for production use, but matching the behavior of common production
+servers reduces the surface area for behavior changes during deployment.
+
+Mitigated possible XSS attack via user-supplied redirect URLs
+=============================================================
+
+Django relies on user input in some cases (e.g.
+:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+to redirect the user to an "on success" URL. The security checks for these
+redirects (namely ``django.util.http.is_safe_url()``) didn't strip leading
+whitespace on the tested URL and as such considered URLs like
+``\njavascript:...`` safe. If a developer relied on ``is_safe_url()`` to
+provide safe redirect targets and put such a URL into a link, they could suffer
+from a XSS attack. This bug doesn't affect Django currently, since we only put
+this URL into the ``Location`` response header and browsers seem to ignore
+JavaScript there.
+
+Denial-of-service attack against ``django.views.static.serve``
+==============================================================
+
+In older versions of Django, the :func:`django.views.static.serve` view read
+the files it served one line at a time. Therefore, a big file with no newlines
+would result in memory usage equal to the size of that file. An attacker could
+exploit this and launch a denial-of-service attack by simultaneously requesting
+many large files. This view now reads the file in chunks to prevent large
+memory usage.
+
+Note, however, that this view has always carried a warning that it is not
+hardened for production use and should be used only as a development aid. Now
+may be a good time to audit your project and serve your files in production
+using a real front-end web server if you are not doing so.
+
+Database denial-of-service with ``ModelMultipleChoiceField``
+============================================================
+
+Given a form that uses ``ModelMultipleChoiceField`` and
+``show_hidden_initial=True`` (not a documented API), it was possible for a user
+to cause an unreasonable number of SQL queries by submitting duplicate values
+for the field's data. The validation logic in ``ModelMultipleChoiceField`` now
+deduplicates submitted values to address this issue.
diff --git a/docs/releases/1.7.3.txt b/docs/releases/1.7.3.txt
new file mode 100644
index 0000000..46785bf
--- /dev/null
+++ b/docs/releases/1.7.3.txt
@@ -0,0 +1,91 @@
+==========================
+Django 1.7.3 release notes
+==========================
+
+*January 13, 2015*
+
+Django 1.7.3 fixes several security issues and bugs in 1.7.2.
+
+WSGI header spoofing via underscore/dash conflation
+===================================================
+
+When HTTP headers are placed into the WSGI environ, they are normalized by
+converting to uppercase, converting all dashes to underscores, and prepending
+`HTTP_`. For instance, a header ``X-Auth-User`` would become
+``HTTP_X_AUTH_USER`` in the WSGI environ (and thus also in Django's
+``request.META`` dictionary).
+
+Unfortunately, this means that the WSGI environ cannot distinguish between
+headers containing dashes and headers containing underscores: ``X-Auth-User``
+and ``X-Auth_User`` both become ``HTTP_X_AUTH_USER``. This means that if a
+header is used in a security-sensitive way (for instance, passing
+authentication information along from a front-end proxy), even if the proxy
+carefully strips any incoming value for ``X-Auth-User``, an attacker may be
+able to provide an ``X-Auth_User`` header (with underscore) and bypass this
+protection.
+
+In order to prevent such attacks, both Nginx and Apache 2.4+ strip all headers
+containing underscores from incoming requests by default. Django's built-in
+development server now does the same. Django's development server is not
+recommended for production use, but matching the behavior of common production
+servers reduces the surface area for behavior changes during deployment.
+
+Mitigated possible XSS attack via user-supplied redirect URLs
+=============================================================
+
+Django relies on user input in some cases (e.g.
+:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+to redirect the user to an "on success" URL. The security checks for these
+redirects (namely ``django.util.http.is_safe_url()``) didn't strip leading
+whitespace on the tested URL and as such considered URLs like
+``\njavascript:...`` safe. If a developer relied on ``is_safe_url()`` to
+provide safe redirect targets and put such a URL into a link, they could suffer
+from a XSS attack. This bug doesn't affect Django currently, since we only put
+this URL into the ``Location`` response header and browsers seem to ignore
+JavaScript there.
+
+Denial-of-service attack against ``django.views.static.serve``
+==============================================================
+
+In older versions of Django, the :func:`django.views.static.serve` view read
+the files it served one line at a time. Therefore, a big file with no newlines
+would result in memory usage equal to the size of that file. An attacker could
+exploit this and launch a denial-of-service attack by simultaneously requesting
+many large files. This view now reads the file in chunks to prevent large
+memory usage.
+
+Note, however, that this view has always carried a warning that it is not
+hardened for production use and should be used only as a development aid. Now
+may be a good time to audit your project and serve your files in production
+using a real front-end web server if you are not doing so.
+
+Database denial-of-service with ``ModelMultipleChoiceField``
+============================================================
+
+Given a form that uses ``ModelMultipleChoiceField`` and
+``show_hidden_initial=True`` (not a documented API), it was possible for a user
+to cause an unreasonable number of SQL queries by submitting duplicate values
+for the field's data. The validation logic in ``ModelMultipleChoiceField`` now
+deduplicates submitted values to address this issue.
+
+Bugfixes
+========
+
+* The default iteration count for the PBKDF2 password hasher has been
+  increased by 25%. This part of the normal major release process was
+  inadvertently omitted in 1.7. This backwards compatible change will not
+  affect users who have subclassed
+  ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` to change the
+  default value.
+
+* Fixed a crash in the CSRF middleware when handling non-ASCII referer header
+  (:ticket:`23815`).
+
+* Fixed a crash in the ``django.contrib.auth.redirect_to_login`` view when
+  passing a :func:`~django.core.urlresolvers.reverse_lazy` result on Python 3
+  (:ticket:`24097`).
+
+* Added correct formats for Greek (``el``) (:ticket:`23967`).
+
+* Fixed a migration crash when unapplying a migration where multiple operations
+  interact with the same model (:ticket:`24110`).
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 5f8fa67..4369ef3 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,7 @@ versions of the documentation contain the release notes for any later releases.
 .. toctree::
    :maxdepth: 1
 
+   1.7.3
    1.7.2
    1.7.1
    1.7
@@ -34,6 +35,7 @@ versions of the documentation contain the release notes for any later releases.
 .. toctree::
    :maxdepth: 1
 
+   1.6.10
    1.6.9
    1.6.8
    1.6.7
@@ -69,6 +71,7 @@ versions of the documentation contain the release notes for any later releases.
 .. toctree::
    :maxdepth: 1
 
+   1.4.18
    1.4.17
    1.4.16
    1.4.15
diff --git a/docs/spelling_wordlist b/docs/spelling_wordlist
index 23465d2..da7ce09 100644
--- a/docs/spelling_wordlist
+++ b/docs/spelling_wordlist
@@ -134,6 +134,7 @@ dbshell
 de
 deconstruct
 deconstructing
+deduplicates
 deepcopy
 deserialization
 deserialize
diff --git a/docs/topics/forms/index.txt b/docs/topics/forms/index.txt
index 1f0c56f..3006c08 100644
--- a/docs/topics/forms/index.txt
+++ b/docs/topics/forms/index.txt
@@ -389,8 +389,8 @@ The distinction between :ref:`ref-forms-api-bound-unbound` is important:
   is valid. If an invalid bound form is rendered, it can include inline error
   messages telling the user what data to correct.
 
-The form's ``is_bound()`` method will tell you whether a form has data bound to
-it or not.
+The form's :attr:`~Form.is_bound` attribute will tell you whether a form has
+data bound to it or not.
 
 More on fields
 --------------
diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt
index 7594cdf..308ed3f 100755
--- a/docs/topics/migrations.txt
+++ b/docs/topics/migrations.txt
@@ -435,6 +435,73 @@ You can pass a second callable to
 want executed when migrating backwards. If this callable is omitted, migrating
 backwards will raise an exception.
 
+.. _data-migrations-and-multiple-databases:
+
+Data migrations and multiple databases
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using multiple databases, you may need to figure out whether or not to
+run a migration against a particular database. For example, you may want to
+**only** run a migration on a particular database.
+
+In order to do that you can check the database connection's alias inside a
+``RunPython`` operation by looking at the ``schema_editor.connection.alias``
+attribute::
+
+    from django.db import migrations
+
+    def forwards(apps, schema_editor):
+        if not schema_editor.connection.alias == 'default':
+            return
+        # Your migration code goes here
+
+    class Migration(migrations.Migration):
+
+        dependencies = [
+            # Dependencies to other migrations
+        ]
+
+        operations = [
+            migrations.RunPython(forwards),
+        ]
+
+You can also use your database router's ``allow_migrate()`` method, but keep in
+mind that the imported router needs to stay around as long as it is referenced
+inside a migration:
+
+.. snippet::
+    :filename: myapp/dbrouters.py
+
+    class MyRouter(object):
+
+        def allow_migrate(self, db, model):
+            return db == 'default'
+
+Then, to leverage this in your migrations, do the following::
+
+    from django.db import migrations
+
+    from myappname.dbrouters import MyRouter
+
+    def forwards(apps, schema_editor):
+        MyModel = apps.get_model("myappname", "MyModel")
+        if not MyRouter().allow_migrate(schema_editor.connection.alias, MyModel):
+            return
+        # Your migration code goes here
+
+    class Migration(migrations.Migration):
+
+        dependencies = [
+            # Dependencies to other migrations
+        ]
+
+        operations = [
+            migrations.RunPython(forwards),
+        ]
+
+More advanced migrations
+~~~~~~~~~~~~~~~~~~~~~~~~
+
 If you're interested in the more advanced migration operations, or want
 to be able to write your own, see the :doc:`migration operations reference
 </ref/migration-operations>`.
diff --git a/tests/csrf_tests/tests.py b/tests/csrf_tests/tests.py
index 6199ca8..a2a8f1c 100644
--- a/tests/csrf_tests/tests.py
+++ b/tests/csrf_tests/tests.py
@@ -300,6 +300,11 @@ class CsrfViewMiddlewareTest(TestCase):
         req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
         self.assertNotEqual(None, req2)
         self.assertEqual(403, req2.status_code)
+        # Non-ASCII
+        req.META['HTTP_REFERER'] = b'\xd8B\xf6I\xdf'
+        req2 = CsrfViewMiddleware().process_view(req, post_form_view, (), {})
+        self.assertNotEqual(None, req2)
... 311 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django.git



More information about the Python-modules-commits mailing list