Просмотр исходного кода

Refs #17209 -- Removed login/logout and password reset/change function-based views.

Per deprecation timeline.
Tim Graham 7 лет назад
Родитель
Сommit
4f313e284e

+ 1 - 223
django/contrib/auth/views.py

@@ -15,8 +15,7 @@ from django.contrib.auth.tokens import default_token_generator
 from django.contrib.sites.shortcuts import get_current_site
 from django.http import HttpResponseRedirect, QueryDict
 from django.shortcuts import resolve_url
-from django.template.response import TemplateResponse
-from django.urls import reverse, reverse_lazy
+from django.urls import reverse_lazy
 from django.utils.decorators import method_decorator
 from django.utils.deprecation import RemovedInDjango21Warning
 from django.utils.http import is_safe_url, urlsafe_base64_decode
@@ -107,23 +106,6 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView):
         return context
 
 
-def login(request, template_name='registration/login.html',
-          redirect_field_name=REDIRECT_FIELD_NAME,
-          authentication_form=AuthenticationForm,
-          extra_context=None, redirect_authenticated_user=False):
-    warnings.warn(
-        'The login() view is superseded by the class-based LoginView().',
-        RemovedInDjango21Warning, stacklevel=2
-    )
-    return LoginView.as_view(
-        template_name=template_name,
-        redirect_field_name=redirect_field_name,
-        form_class=authentication_form,
-        extra_context=extra_context,
-        redirect_authenticated_user=redirect_authenticated_user,
-    )(request)
-
-
 class LogoutView(SuccessURLAllowedHostsMixin, TemplateView):
     """
     Log out the user and display the 'You are logged out' message.
@@ -184,22 +166,6 @@ class LogoutView(SuccessURLAllowedHostsMixin, TemplateView):
         return context
 
 
-def logout(request, next_page=None,
-           template_name='registration/logged_out.html',
-           redirect_field_name=REDIRECT_FIELD_NAME,
-           extra_context=None):
-    warnings.warn(
-        'The logout() view is superseded by the class-based LogoutView().',
-        RemovedInDjango21Warning, stacklevel=2
-    )
-    return LogoutView.as_view(
-        next_page=next_page,
-        template_name=template_name,
-        redirect_field_name=redirect_field_name,
-        extra_context=extra_context,
-    )(request)
-
-
 _sentinel = object()
 
 
@@ -234,143 +200,6 @@ def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_N
     return HttpResponseRedirect(urlunparse(login_url_parts))
 
 
-# 4 views for password reset:
-# - password_reset sends the mail
-# - password_reset_done shows a success message for the above
-# - password_reset_confirm checks the link the user clicked and
-#   prompts for a new password
-# - password_reset_complete shows a success message for the above
-
-@csrf_protect
-def password_reset(request,
-                   template_name='registration/password_reset_form.html',
-                   email_template_name='registration/password_reset_email.html',
-                   subject_template_name='registration/password_reset_subject.txt',
-                   password_reset_form=PasswordResetForm,
-                   token_generator=default_token_generator,
-                   post_reset_redirect=None,
-                   from_email=None,
-                   extra_context=None,
-                   html_email_template_name=None,
-                   extra_email_context=None):
-    warnings.warn("The password_reset() view is superseded by the "
-                  "class-based PasswordResetView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    if post_reset_redirect is None:
-        post_reset_redirect = reverse('password_reset_done')
-    else:
-        post_reset_redirect = resolve_url(post_reset_redirect)
-    if request.method == "POST":
-        form = password_reset_form(request.POST)
-        if form.is_valid():
-            opts = {
-                'use_https': request.is_secure(),
-                'token_generator': token_generator,
-                'from_email': from_email,
-                'email_template_name': email_template_name,
-                'subject_template_name': subject_template_name,
-                'request': request,
-                'html_email_template_name': html_email_template_name,
-                'extra_email_context': extra_email_context,
-            }
-            form.save(**opts)
-            return HttpResponseRedirect(post_reset_redirect)
-    else:
-        form = password_reset_form()
-    context = {
-        'form': form,
-        'title': _('Password reset'),
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
-def password_reset_done(request,
-                        template_name='registration/password_reset_done.html',
-                        extra_context=None):
-    warnings.warn("The password_reset_done() view is superseded by the "
-                  "class-based PasswordResetDoneView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    context = {
-        'title': _('Password reset sent'),
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
-# Doesn't need csrf_protect since no-one can guess the URL
-@sensitive_post_parameters()
-@never_cache
-def password_reset_confirm(request, uidb64=None, token=None,
-                           template_name='registration/password_reset_confirm.html',
-                           token_generator=default_token_generator,
-                           set_password_form=SetPasswordForm,
-                           post_reset_redirect=None,
-                           extra_context=None):
-    """
-    Check the hash in a password reset link and present a form for entering a
-    new password.
-    """
-    warnings.warn("The password_reset_confirm() view is superseded by the "
-                  "class-based PasswordResetConfirmView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    assert uidb64 is not None and token is not None  # checked by URLconf
-    if post_reset_redirect is None:
-        post_reset_redirect = reverse('password_reset_complete')
-    else:
-        post_reset_redirect = resolve_url(post_reset_redirect)
-    try:
-        # urlsafe_base64_decode() decodes to bytestring
-        uid = urlsafe_base64_decode(uidb64).decode()
-        user = UserModel._default_manager.get(pk=uid)
-    except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
-        user = None
-
-    if user is not None and token_generator.check_token(user, token):
-        validlink = True
-        title = _('Enter new password')
-        if request.method == 'POST':
-            form = set_password_form(user, request.POST)
-            if form.is_valid():
-                form.save()
-                return HttpResponseRedirect(post_reset_redirect)
-        else:
-            form = set_password_form(user)
-    else:
-        validlink = False
-        form = None
-        title = _('Password reset unsuccessful')
-    context = {
-        'form': form,
-        'title': title,
-        'validlink': validlink,
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
-def password_reset_complete(request,
-                            template_name='registration/password_reset_complete.html',
-                            extra_context=None):
-    warnings.warn("The password_reset_complete() view is superseded by the "
-                  "class-based PasswordResetCompleteView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    context = {
-        'login_url': resolve_url(settings.LOGIN_URL),
-        'title': _('Password reset complete'),
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
 # Class-based password reset views
 # - PasswordResetView sends the mail
 # - PasswordResetDoneView shows a success message for the above
@@ -511,57 +340,6 @@ class PasswordResetCompleteView(PasswordContextMixin, TemplateView):
         return context
 
 
-@sensitive_post_parameters()
-@csrf_protect
-@login_required
-def password_change(request,
-                    template_name='registration/password_change_form.html',
-                    post_change_redirect=None,
-                    password_change_form=PasswordChangeForm,
-                    extra_context=None):
-    warnings.warn("The password_change() view is superseded by the "
-                  "class-based PasswordChangeView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    if post_change_redirect is None:
-        post_change_redirect = reverse('password_change_done')
-    else:
-        post_change_redirect = resolve_url(post_change_redirect)
-    if request.method == "POST":
-        form = password_change_form(user=request.user, data=request.POST)
-        if form.is_valid():
-            form.save()
-            # Updating the password logs out all other sessions for the user
-            # except the current one.
-            update_session_auth_hash(request, form.user)
-            return HttpResponseRedirect(post_change_redirect)
-    else:
-        form = password_change_form(user=request.user)
-    context = {
-        'form': form,
-        'title': _('Password change'),
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
-@login_required
-def password_change_done(request,
-                         template_name='registration/password_change_done.html',
-                         extra_context=None):
-    warnings.warn("The password_change_done() view is superseded by the "
-                  "class-based PasswordChangeDoneView().",
-                  RemovedInDjango21Warning, stacklevel=2)
-    context = {
-        'title': _('Password change successful'),
-    }
-    if extra_context is not None:
-        context.update(extra_context)
-
-    return TemplateResponse(request, template_name, context)
-
-
 class PasswordChangeView(PasswordContextMixin, FormView):
     form_class = PasswordChangeForm
     success_url = reverse_lazy('password_change_done')

+ 1 - 1
docs/internals/deprecation.txt

@@ -714,7 +714,7 @@ details on these changes.
   remove calls to this method, and instead ensure that their auth related views
   are CSRF protected, which ensures that cookies are enabled.
 
-* The version of :func:`django.contrib.auth.views.password_reset_confirm` that
+* The version of ``django.contrib.auth.views.password_reset_confirm()`` that
   supports base36 encoded user IDs
   (``django.contrib.auth.views.password_reset_confirm_uidb36``) will be
   removed. If your site has been running Django 1.6 for more than

+ 1 - 1
docs/releases/1.10.7.txt

@@ -10,7 +10,7 @@ CVE-2017-7233: Open redirect and possible XSS attack via user-supplied numeric r
 ============================================================================================
 
 Django relies on user input in some cases  (e.g.
-:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+``django.contrib.auth.views.login()`` and :doc:`i18n </topics/i18n/index>`)
 to redirect the user to an "on success" URL. The security check for these
 redirects (namely ``django.utils.http.is_safe_url()``) considered some numeric
 URLs (e.g. ``http:999999999``) "safe" when they shouldn't be.

+ 4 - 4
docs/releases/1.10.txt

@@ -118,19 +118,19 @@ Minor features
   subclassed ``django.contrib.auth.hashers.PBKDF2PasswordHasher`` to change the
   default value.
 
-* The :func:`~django.contrib.auth.views.logout` view sends "no-cache" headers
+* The ``django.contrib.auth.views.logout()`` view sends "no-cache" headers
   to prevent an issue where Safari caches redirects and prevents a user from
   being able to log out.
 
-* Added the optional ``backend`` argument to :func:`~django.contrib.auth.login`
+* Added the optional ``backend`` argument to :func:`django.contrib.auth.login`
   to allow using it without credentials.
 
 * The new :setting:`LOGOUT_REDIRECT_URL` setting controls the redirect of the
-  :func:`~django.contrib.auth.views.logout` view, if the view doesn't get a
+  ``django.contrib.auth.views.logout()`` view, if the view doesn't get a
   ``next_page`` argument.
 
 * The new ``redirect_authenticated_user`` parameter for the
-  :func:`~django.contrib.auth.views.login` view allows redirecting
+  ``django.contrib.auth.views.login()`` view allows redirecting
   authenticated users visiting the login page.
 
 * The new :class:`~django.contrib.auth.backends.AllowAllUsersModelBackend` and

+ 1 - 1
docs/releases/1.3.txt

@@ -636,7 +636,7 @@ message as a nonexistent account.
 Password reset view now accepts ``from_email``
 ----------------------------------------------
 
-The :func:`django.contrib.auth.views.password_reset` view now accepts a
+The ``django.contrib.auth.views.password_reset()`` view now accepts a
 ``from_email`` parameter, which is passed to the ``password_reset_form``’s
 ``save()`` method as a keyword argument. If you are using this view with a
 custom password reset form, then you will need to ensure your form's ``save()``

+ 1 - 1
docs/releases/1.4.13.txt

@@ -36,7 +36,7 @@ which are accepted by some browsers. This allows a user to be redirected to
 an unsafe URL unexpectedly.
 
 Django relies on user input in some cases (e.g.
-:func:`django.contrib.auth.views.login`, ``django.contrib.comments``, and
+``django.contrib.auth.views.login()``, ``django.contrib.comments``, and
 :doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
 The security checks for these redirects (namely
 ``django.utils.http.is_safe_url()``) did not correctly validate some malformed

+ 1 - 1
docs/releases/1.4.18.txt

@@ -35,7 +35,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) didn't strip leading
 whitespace on the tested URL and as such considered URLs like

+ 1 - 1
docs/releases/1.4.20.txt

@@ -10,7 +10,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) accepted URLs with
 leading control characters and so considered URLs like ``\x08javascript:...``

+ 1 - 1
docs/releases/1.4.22.txt

@@ -14,7 +14,7 @@ Denial-of-service possibility in ``logout()`` view by filling session store
 ===========================================================================
 
 Previously, a session could be created when anonymously accessing the
-:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated
+``django.contrib.auth.views.logout()`` view (provided it wasn't decorated
 with :func:`~django.contrib.auth.decorators.login_required` as done in the
 admin). This could allow an attacker to easily create many new session records
 by sending repeated requests, potentially filling up the session store or

+ 1 - 1
docs/releases/1.4.6.txt

@@ -13,7 +13,7 @@ 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`, ``django.contrib.comments``, and
+``django.contrib.auth.views.login()``, ``django.contrib.comments``, and
 :doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
 The security checks for these redirects (namely
 ``django.utils.http.is_safe_url()``) didn't check if the scheme is ``http(s)``

+ 1 - 1
docs/releases/1.5.2.txt

@@ -10,7 +10,7 @@ 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`, ``django.contrib.comments``, and
+``django.contrib.auth.views.login()``, ``django.contrib.comments``, and
 :doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
 The security checks for these redirects (namely
 ``django.utils.http.is_safe_url()``) didn't check if the scheme is ``http(s)``

+ 1 - 1
docs/releases/1.5.8.txt

@@ -36,7 +36,7 @@ which are accepted by some browsers. This allows a user to be redirected to
 an unsafe URL unexpectedly.
 
 Django relies on user input in some cases (e.g.
-:func:`django.contrib.auth.views.login`, ``django.contrib.comments``, and
+``django.contrib.auth.views.login()``, ``django.contrib.comments``, and
 :doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
 The security checks for these redirects (namely
 ``django.utils.http.is_safe_url()``) did not correctly validate some malformed

+ 1 - 1
docs/releases/1.6.10.txt

@@ -34,7 +34,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) didn't strip leading
 whitespace on the tested URL and as such considered URLs like

+ 1 - 1
docs/releases/1.6.11.txt

@@ -27,7 +27,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) accepted URLs with
 leading control characters and so considered URLs like ``\x08javascript:...``

+ 1 - 1
docs/releases/1.6.5.txt

@@ -36,7 +36,7 @@ which are accepted by some browsers. This allows a user to be redirected to
 an unsafe URL unexpectedly.
 
 Django relies on user input in some cases (e.g.
-:func:`django.contrib.auth.views.login`, ``django.contrib.comments``, and
+``django.contrib.auth.views.login()``, ``django.contrib.comments``, and
 :doc:`i18n </topics/i18n/index>`) to redirect the user to an "on success" URL.
 The security checks for these redirects (namely
 ``django.utils.http.is_safe_url()``) did not correctly validate some malformed

+ 7 - 7
docs/releases/1.6.txt

@@ -280,10 +280,10 @@ Minor features
   :attr:`~django.http.HttpResponse.reason_phrase`.
 
 * When giving the URL of the next page for
-  :func:`~django.contrib.auth.views.logout`,
-  :func:`~django.contrib.auth.views.password_reset`,
-  :func:`~django.contrib.auth.views.password_reset_confirm`,
-  and :func:`~django.contrib.auth.views.password_change`, you can now pass
+  ``django.contrib.auth.views.logout()``,
+  ``django.contrib.auth.views.password_reset()``,
+  ``django.contrib.auth.views.password_reset_confirm()``,
+  and ``django.contrib.auth.views.password_change()``, you can now pass
   URL names and they will be resolved.
 
 * The new :option:`dumpdata --pks` option specifies the primary keys of objects
@@ -742,17 +742,17 @@ can set the
 
 Past versions of Django used base 36 encoding of the ``User`` primary key in
 the password reset views and URLs
-(:func:`django.contrib.auth.views.password_reset_confirm`). Base 36 encoding is
+(``django.contrib.auth.views.password_reset_confirm()``). Base 36 encoding is
 sufficient if the user primary key is an integer, however, with the
 introduction of custom user models in Django 1.5, that assumption may no longer
 be true.
 
-:func:`django.contrib.auth.views.password_reset_confirm` has been modified to
+``django.contrib.auth.views.password_reset_confirm()`` has been modified to
 take a ``uidb64`` parameter instead of ``uidb36``. If you are reversing this
 view, for example in a custom ``password_reset_email.html`` template, be sure
 to update your code.
 
-A temporary shim for :func:`django.contrib.auth.views.password_reset_confirm`
+A temporary shim for ``django.contrib.auth.views.password_reset_confirm()``
 that will allow password reset links generated prior to Django 1.6 to continue
 to work has been added to provide backwards compatibility; this will be removed
 in Django 1.7. Thus, as long as your site has been running Django 1.6 for more

+ 1 - 1
docs/releases/1.7.10.txt

@@ -10,7 +10,7 @@ Denial-of-service possibility in ``logout()`` view by filling session store
 ===========================================================================
 
 Previously, a session could be created when anonymously accessing the
-:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated
+``django.contrib.auth.views.logout()`` view (provided it wasn't decorated
 with :func:`~django.contrib.auth.decorators.login_required` as done in the
 admin). This could allow an attacker to easily create many new session records
 by sending repeated requests, potentially filling up the session store or

+ 1 - 1
docs/releases/1.7.3.txt

@@ -34,7 +34,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) didn't strip leading
 whitespace on the tested URL and as such considered URLs like

+ 1 - 1
docs/releases/1.7.7.txt

@@ -27,7 +27,7 @@ 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>`)
+``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.utils.http.is_safe_url()``) accepted URLs with
 leading control characters and so considered URLs like ``\x08javascript:...``

+ 2 - 2
docs/releases/1.7.txt

@@ -421,7 +421,7 @@ Minor features
   <django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed>` method
   to more easily customize the login policy.
 
-* :func:`django.contrib.auth.views.password_reset` takes an optional
+* ``django.contrib.auth.views.password_reset()`` takes an optional
   ``html_email_template_name`` parameter used to send a multipart HTML email
   for password resets.
 
@@ -1846,7 +1846,7 @@ remove usage of these features.
 * The ``check_for_test_cookie`` method in
   :class:`~django.contrib.auth.forms.AuthenticationForm` is removed.
 
-* The version of :func:`django.contrib.auth.views.password_reset_confirm` that
+* The version of ``django.contrib.auth.views.password_reset_confirm()`` that
   supports base36 encoded user IDs
   (``django.contrib.auth.views.password_reset_confirm_uidb36``) is removed.
 

+ 1 - 1
docs/releases/1.8.10.txt

@@ -10,7 +10,7 @@ CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redi
 ===============================================================================================================
 
 Django relies on user input in some cases (e.g.
-:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+``django.contrib.auth.views.login()`` and :doc:`i18n </topics/i18n/index>`)
 to redirect the user to an "on success" URL. The security check for these
 redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs
 with basic authentication credentials "safe" when they shouldn't be.

+ 1 - 1
docs/releases/1.8.18.txt

@@ -10,7 +10,7 @@ CVE-2017-7233: Open redirect and possible XSS attack via user-supplied numeric r
 ============================================================================================
 
 Django relies on user input in some cases  (e.g.
-:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+``django.contrib.auth.views.login()`` and :doc:`i18n </topics/i18n/index>`)
 to redirect the user to an "on success" URL. The security check for these
 redirects (namely ``django.utils.http.is_safe_url()``) considered some numeric
 URLs (e.g. ``http:999999999``) "safe" when they shouldn't be.

+ 1 - 1
docs/releases/1.8.4.txt

@@ -10,7 +10,7 @@ Denial-of-service possibility in ``logout()`` view by filling session store
 ===========================================================================
 
 Previously, a session could be created when anonymously accessing the
-:func:`django.contrib.auth.views.logout` view (provided it wasn't decorated
+``django.contrib.auth.views.logout()`` view (provided it wasn't decorated
 with :func:`~django.contrib.auth.decorators.login_required` as done in the
 admin). This could allow an attacker to easily create many new session records
 by sending repeated requests, potentially filling up the session store or

+ 1 - 1
docs/releases/1.9.13.txt

@@ -11,7 +11,7 @@ CVE-2017-7233: Open redirect and possible XSS attack via user-supplied numeric r
 ============================================================================================
 
 Django relies on user input in some cases  (e.g.
-:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+``django.contrib.auth.views.login()`` and :doc:`i18n </topics/i18n/index>`)
 to redirect the user to an "on success" URL. The security check for these
 redirects (namely ``django.utils.http.is_safe_url()``) considered some numeric
 URLs (e.g. ``http:999999999``) "safe" when they shouldn't be.

+ 1 - 1
docs/releases/1.9.3.txt

@@ -10,7 +10,7 @@ CVE-2016-2512: Malicious redirect and possible XSS attack via user-supplied redi
 ===============================================================================================================
 
 Django relies on user input in some cases (e.g.
-:func:`django.contrib.auth.views.login` and :doc:`i18n </topics/i18n/index>`)
+``django.contrib.auth.views.login()`` and :doc:`i18n </topics/i18n/index>`)
 to redirect the user to an "on success" URL. The security check for these
 redirects (namely ``django.utils.http.is_safe_url()``) considered some URLs
 with basic authentication credentials "safe" when they shouldn't be.

+ 1 - 1
docs/releases/1.9.txt

@@ -216,7 +216,7 @@ Minor features
   makes it possible to use ``REMOTE_USER`` for setups where the header is only
   populated on login pages instead of every request in the session.
 
-* The :func:`~django.contrib.auth.views.password_reset` view accepts an
+* The ``django.contrib.auth.views.password_reset()`` view accepts an
   ``extra_email_context`` parameter.
 
 :mod:`django.contrib.contenttypes`

+ 6 - 0
docs/releases/2.1.txt

@@ -223,3 +223,9 @@ Features removed in 2.1
 These features have reached the end of their deprecation cycle and are removed
 in Django 2.1. See :ref:`deprecated-features-1.11` for details, including how
 to remove usage of these features.
+in Django 2.1. See :ref:`deprecated-features-1.11` and for details, including
+how to remove usage of these features.
+
+* ``contrib.auth.views.login()``, ``logout()``, ``password_change()``,
+  ``password_change_done()``, ``password_reset()``, ``password_reset_done()``,
+  ``password_reset_confirm()``, and ``password_reset_complete()`` are removed.

+ 0 - 86
docs/topics/auth/default.txt

@@ -945,16 +945,6 @@ All authentication views
 This is a list with all the views ``django.contrib.auth`` provides. For
 implementation details see :ref:`using-the-views`.
 
-.. function:: login(request, template_name=`registration/login.html`, redirect_field_name='next', authentication_form=AuthenticationForm, extra_context=None, redirect_authenticated_user=False)
-
-    .. deprecated:: 1.11
-
-        The ``login`` function-based view should be replaced by the class-based
-        :class:`LoginView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``LoginView`` attributes.
-
 .. class:: LoginView
 
     .. versionadded:: 1.11
@@ -1093,16 +1083,6 @@ implementation details see :ref:`using-the-views`.
     ``get_user()`` method which returns the authenticated user object (this
     method is only ever called after successful form validation).
 
-.. function:: logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name='next', extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``logout`` function-based view should be replaced by the
-        class-based :class:`LogoutView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``LogoutView`` attributes.
-
 .. class:: LogoutView
 
     .. versionadded:: 1.11
@@ -1165,18 +1145,6 @@ implementation details see :ref:`using-the-views`.
         The unused ``extra_context`` parameter is deprecated and will be
         removed in Django 2.1.
 
-.. function:: password_change(request, template_name='registration/password_change_form.html', post_change_redirect=None, password_change_form=PasswordChangeForm, extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_change`` function-based view should be replaced by the
-        class-based :class:`PasswordChangeView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordChangeView`` attributes, except the ``post_change_redirect`` and
-    ``password_change_form`` arguments which map to the ``success_url`` and
-    ``form_class`` attributes of the class-based view.
-
 .. class:: PasswordChangeView
 
     .. versionadded:: 1.11
@@ -1206,16 +1174,6 @@ implementation details see :ref:`using-the-views`.
 
     * ``form``: The password change form (see ``form_class`` above).
 
-.. function:: password_change_done(request, template_name='registration/password_change_done.html', extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_change_done`` function-based view should be replaced by
-        the class-based :class:`PasswordChangeDoneView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordChangeDoneView`` attributes.
-
 .. class:: PasswordChangeDoneView
 
     .. versionadded:: 1.11
@@ -1233,18 +1191,6 @@ implementation details see :ref:`using-the-views`.
     * ``extra_context``: A dictionary of context data that will be added to the
       default context data passed to the template.
 
-.. function:: password_reset(request, template_name='registration/password_reset_form.html', email_template_name='registration/password_reset_email.html', subject_template_name='registration/password_reset_subject.txt', password_reset_form=PasswordResetForm, token_generator=default_token_generator, post_reset_redirect=None, from_email=None, extra_context=None, html_email_template_name=None, extra_email_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_reset`` function-based view should be replaced by the
-        class-based :class:`PasswordResetView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordResetView`` attributes, except the ``post_reset_redirect`` and
-    ``password_reset_form`` arguments which map to the ``success_url`` and
-    ``form_class`` attributes of the class-based view.
-
 .. class:: PasswordResetView
 
     .. versionadded:: 1.11
@@ -1345,16 +1291,6 @@ implementation details see :ref:`using-the-views`.
     The same template context is used for subject template. Subject must be
     single line plain text string.
 
-.. function:: password_reset_done(request, template_name='registration/password_reset_done.html', extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_reset_done`` function-based view should be replaced by
-        the  class-based :class:`PasswordResetDoneView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordResetDoneView`` attributes.
-
 .. class:: PasswordResetDoneView
 
     .. versionadded:: 1.11
@@ -1380,18 +1316,6 @@ implementation details see :ref:`using-the-views`.
     * ``extra_context``: A dictionary of context data that will be added to the
       default context data passed to the template.
 
-.. function:: password_reset_confirm(request, uidb64=None, token=None, template_name='registration/password_reset_confirm.html', token_generator=default_token_generator, set_password_form=SetPasswordForm, post_reset_redirect=None, extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_reset_confirm`` function-based view should be replaced by
-        the class-based :class:`PasswordResetConfirmView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordResetConfirmView`` attributes, except the ``post_reset_redirect``
-    and ``set_password_form`` arguments which map to the ``success_url`` and
-    ``form_class`` attributes of the class-based view.
-
 .. class:: PasswordResetConfirmView
 
     .. versionadded:: 1.11
@@ -1442,16 +1366,6 @@ implementation details see :ref:`using-the-views`.
     * ``validlink``: Boolean, True if the link (combination of ``uidb64`` and
       ``token``) is valid or unused yet.
 
-.. function:: password_reset_complete(request, template_name='registration/password_reset_complete.html', extra_context=None)
-
-    .. deprecated:: 1.11
-
-        The ``password_reset_complete`` function-based view should be replaced
-        by the class-based :class:`PasswordResetCompleteView`.
-
-    The optional arguments of this view are similar to the class-based
-    ``PasswordResetCompleteView`` attributes.
-
 .. class:: PasswordResetCompleteView
 
     .. versionadded:: 1.11

+ 0 - 488
tests/auth_tests/test_deprecated_views.py

@@ -1,488 +0,0 @@
-import datetime
-import itertools
-import re
-from urllib.parse import ParseResult, urlparse
-
-from django.conf import settings
-from django.contrib.auth import SESSION_KEY
-from django.contrib.auth.forms import (
-    AuthenticationForm, PasswordChangeForm, SetPasswordForm,
-)
-from django.contrib.auth.models import User
-from django.contrib.auth.views import login, logout
-from django.core import mail
-from django.http import QueryDict
-from django.test import RequestFactory, TestCase, override_settings
-from django.test.utils import ignore_warnings, patch_logger
-from django.utils.deprecation import RemovedInDjango21Warning
-
-from .models import CustomUser, UUIDUser
-from .settings import AUTH_TEMPLATES
-
-
-@override_settings(
-    LANGUAGES=[('en', 'English')],
-    LANGUAGE_CODE='en',
-    TEMPLATES=AUTH_TEMPLATES,
-    ROOT_URLCONF='auth_tests.urls_deprecated',
-)
-class AuthViewsTestCase(TestCase):
-    """
-    Helper base class for all the follow test cases.
-    """
-
-    @classmethod
-    def setUpTestData(cls):
-        cls.u1 = User.objects.create_user(username='testclient', password='password', email='testclient@example.com')
-        cls.u3 = User.objects.create_user(username='staff', password='password', email='staffmember@example.com')
-
-    def login(self, username='testclient', password='password'):
-        response = self.client.post('/login/', {
-            'username': username,
-            'password': password,
-        })
-        self.assertIn(SESSION_KEY, self.client.session)
-        return response
-
-    def logout(self):
-        response = self.client.get('/admin/logout/')
-        self.assertEqual(response.status_code, 200)
-        self.assertNotIn(SESSION_KEY, self.client.session)
-
-    def assertFormError(self, response, error):
-        """Assert that error is found in response.context['form'] errors"""
-        form_errors = list(itertools.chain(*response.context['form'].errors.values()))
-        self.assertIn(str(error), form_errors)
-
-    def assertURLEqual(self, url, expected, parse_qs=False):
-        """
-        Given two URLs, make sure all their components (the ones given by
-        urlparse) are equal, only comparing components that are present in both
-        URLs.
-        If `parse_qs` is True, then the querystrings are parsed with QueryDict.
-        This is useful if you don't want the order of parameters to matter.
-        Otherwise, the query strings are compared as-is.
-        """
-        fields = ParseResult._fields
-
-        for attr, x, y in zip(fields, urlparse(url), urlparse(expected)):
-            if parse_qs and attr == 'query':
-                x, y = QueryDict(x), QueryDict(y)
-            if x and y and x != y:
-                self.fail("%r != %r (%s doesn't match)" % (url, expected, attr))
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-class PasswordResetTest(AuthViewsTestCase):
-
-    def test_email_not_found(self):
-        """If the provided email is not registered, don't raise any error but
-        also don't send any email."""
-        response = self.client.get('/password_reset/')
-        self.assertEqual(response.status_code, 200)
-        response = self.client.post('/password_reset/', {'email': 'not_a_real_email@email.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 0)
-
-    def test_email_found(self):
-        "Email is sent if a valid email address is provided for password reset"
-        response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 1)
-        self.assertIn("http://", mail.outbox[0].body)
-        self.assertEqual(settings.DEFAULT_FROM_EMAIL, mail.outbox[0].from_email)
-        # optional multipart text/html email has been added.  Make sure original,
-        # default functionality is 100% the same
-        self.assertFalse(mail.outbox[0].message().is_multipart())
-
-    def test_extra_email_context(self):
-        """
-        extra_email_context should be available in the email template context.
-        """
-        response = self.client.post(
-            '/password_reset_extra_email_context/',
-            {'email': 'staffmember@example.com'},
-        )
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 1)
-        self.assertIn('Email email context: "Hello!"', mail.outbox[0].body)
-
-    def test_html_mail_template(self):
-        """
-        A multipart email with text/plain and text/html is sent
-        if the html_email_template parameter is passed to the view
-        """
-        response = self.client.post('/password_reset/html_email_template/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 1)
-        message = mail.outbox[0].message()
-        self.assertEqual(len(message.get_payload()), 2)
-        self.assertTrue(message.is_multipart())
-        self.assertEqual(message.get_payload(0).get_content_type(), 'text/plain')
-        self.assertEqual(message.get_payload(1).get_content_type(), 'text/html')
-        self.assertNotIn('<html>', message.get_payload(0).get_payload())
-        self.assertIn('<html>', message.get_payload(1).get_payload())
-
-    def test_email_found_custom_from(self):
-        "Email is sent if a valid email address is provided for password reset when a custom from_email is provided."
-        response = self.client.post('/password_reset_from_email/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 1)
-        self.assertEqual("staffmember@example.com", mail.outbox[0].from_email)
-
-    # Skip any 500 handler action (like sending more mail...)
-    @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True)
-    def test_poisoned_http_host(self):
-        "Poisoned HTTP_HOST headers can't be used for reset emails"
-        # This attack is based on the way browsers handle URLs. The colon
-        # should be used to separate the port, but if the URL contains an @,
-        # the colon is interpreted as part of a username for login purposes,
-        # making 'evil.com' the request domain. Since HTTP_HOST is used to
-        # produce a meaningful reset URL, we need to be certain that the
-        # HTTP_HOST header isn't poisoned. This is done as a check when get_host()
-        # is invoked, but we check here as a practical consequence.
-        with patch_logger('django.security.DisallowedHost', 'error') as logger_calls:
-            response = self.client.post(
-                '/password_reset/',
-                {'email': 'staffmember@example.com'},
-                HTTP_HOST='www.example:dr.frankenstein@evil.tld'
-            )
-            self.assertEqual(response.status_code, 400)
-            self.assertEqual(len(mail.outbox), 0)
-            self.assertEqual(len(logger_calls), 1)
-
-    # Skip any 500 handler action (like sending more mail...)
-    @override_settings(DEBUG_PROPAGATE_EXCEPTIONS=True)
-    def test_poisoned_http_host_admin_site(self):
-        "Poisoned HTTP_HOST headers can't be used for reset emails on admin views"
-        with patch_logger('django.security.DisallowedHost', 'error') as logger_calls:
-            response = self.client.post(
-                '/admin_password_reset/',
-                {'email': 'staffmember@example.com'},
-                HTTP_HOST='www.example:dr.frankenstein@evil.tld'
-            )
-            self.assertEqual(response.status_code, 400)
-            self.assertEqual(len(mail.outbox), 0)
-            self.assertEqual(len(logger_calls), 1)
-
-    def _test_confirm_start(self):
-        # Start by creating the email
-        self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
-        self.assertEqual(len(mail.outbox), 1)
-        return self._read_signup_email(mail.outbox[0])
-
-    def _read_signup_email(self, email):
-        urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body)
-        self.assertIsNotNone(urlmatch, "No URL found in sent email")
-        return urlmatch.group(), urlmatch.groups()[0]
-
-    def test_confirm_valid(self):
-        url, path = self._test_confirm_start()
-        response = self.client.get(path)
-        # redirect to a 'complete' page:
-        self.assertContains(response, "Please enter your new password")
-
-    def test_confirm_invalid(self):
-        url, path = self._test_confirm_start()
-        # Let's munge the token in the path, but keep the same length,
-        # in case the URLconf will reject a different length.
-        path = path[:-5] + ("0" * 4) + path[-1]
-
-        response = self.client.get(path)
-        self.assertContains(response, "The password reset link was invalid")
-
-    def test_confirm_invalid_user(self):
-        # We get a 200 response for a nonexistent user, not a 404
-        response = self.client.get('/reset/123456/1-1/')
-        self.assertContains(response, "The password reset link was invalid")
-
-    def test_confirm_overflow_user(self):
-        # We get a 200 response for a base36 user id that overflows int
-        response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/')
-        self.assertContains(response, "The password reset link was invalid")
-
-    def test_confirm_invalid_post(self):
-        # Same as test_confirm_invalid, but trying
-        # to do a POST instead.
-        url, path = self._test_confirm_start()
-        path = path[:-5] + ("0" * 4) + path[-1]
-
-        self.client.post(path, {
-            'new_password1': 'anewpassword',
-            'new_password2': ' anewpassword',
-        })
-        # Check the password has not been changed
-        u = User.objects.get(email='staffmember@example.com')
-        self.assertTrue(not u.check_password("anewpassword"))
-
-    def test_confirm_complete(self):
-        url, path = self._test_confirm_start()
-        response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'})
-        # Check the password has been changed
-        u = User.objects.get(email='staffmember@example.com')
-        self.assertTrue(u.check_password("anewpassword"))
-
-        # Check we can't use the link again
-        response = self.client.get(path)
-        self.assertContains(response, "The password reset link was invalid")
-
-    def test_confirm_different_passwords(self):
-        url, path = self._test_confirm_start()
-        response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'x'})
-        self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch'])
-
-    def test_reset_redirect_default(self):
-        response = self.client.post('/password_reset/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_reset/done/')
-
-    def test_reset_custom_redirect(self):
-        response = self.client.post('/password_reset/custom_redirect/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/custom/')
-
-    def test_reset_custom_redirect_named(self):
-        response = self.client.post('/password_reset/custom_redirect/named/', {'email': 'staffmember@example.com'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_reset/')
-
-    def test_confirm_redirect_default(self):
-        url, path = self._test_confirm_start()
-        response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/reset/done/')
-
-    def test_confirm_redirect_custom(self):
-        url, path = self._test_confirm_start()
-        path = path.replace('/reset/', '/reset/custom/')
-        response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/custom/')
-
-    def test_confirm_redirect_custom_named(self):
-        url, path = self._test_confirm_start()
-        path = path.replace('/reset/', '/reset/custom/named/')
-        response = self.client.post(path, {'new_password1': 'anewpassword', 'new_password2': 'anewpassword'})
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_reset/')
-
-    def test_confirm_display_user_from_form(self):
-        url, path = self._test_confirm_start()
-        response = self.client.get(path)
-
-        # #16919 -- The ``password_reset_confirm`` view should pass the user
-        # object to the ``SetPasswordForm``, even on GET requests.
-        # For this test, we render ``{{ form.user }}`` in the template
-        # ``registration/password_reset_confirm.html`` so that we can test this.
-        username = User.objects.get(email='staffmember@example.com').username
-        self.assertContains(response, "Hello, %s." % username)
-
-        # However, the view should NOT pass any user object on a form if the
-        # password reset link was invalid.
-        response = self.client.get('/reset/zzzzzzzzzzzzz/1-1/')
-        self.assertContains(response, "Hello, .")
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-@override_settings(AUTH_USER_MODEL='auth_tests.CustomUser')
-class CustomUserPasswordResetTest(AuthViewsTestCase):
-    user_email = 'staffmember@example.com'
-
-    @classmethod
-    def setUpTestData(cls):
-        cls.u1 = CustomUser.custom_objects.create(
-            email='staffmember@example.com',
-            date_of_birth=datetime.date(1976, 11, 8),
-        )
-        cls.u1.set_password('password')
-        cls.u1.save()
-
-    def _test_confirm_start(self):
-        # Start by creating the email
-        response = self.client.post('/password_reset/', {'email': self.user_email})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(mail.outbox), 1)
-        return self._read_signup_email(mail.outbox[0])
-
-    def _read_signup_email(self, email):
-        urlmatch = re.search(r"https?://[^/]*(/.*reset/\S*)", email.body)
-        self.assertIsNotNone(urlmatch, "No URL found in sent email")
-        return urlmatch.group(), urlmatch.groups()[0]
-
-    def test_confirm_valid_custom_user(self):
-        url, path = self._test_confirm_start()
-        response = self.client.get(path)
-        # redirect to a 'complete' page:
-        self.assertContains(response, "Please enter your new password")
-        # then submit a new password
-        response = self.client.post(path, {
-            'new_password1': 'anewpassword',
-            'new_password2': 'anewpassword',
-        })
-        self.assertRedirects(response, '/reset/done/')
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-@override_settings(AUTH_USER_MODEL='auth_tests.UUIDUser')
-class UUIDUserPasswordResetTest(CustomUserPasswordResetTest):
-
-    def _test_confirm_start(self):
-        # instead of fixture
-        UUIDUser.objects.create_user(
-            email=self.user_email,
-            username='foo',
-            password='foo',
-        )
-        return super()._test_confirm_start()
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-class ChangePasswordTest(AuthViewsTestCase):
-
-    def fail_login(self, password='password'):
-        response = self.client.post('/login/', {
-            'username': 'testclient',
-            'password': password,
-        })
-        self.assertFormError(response, AuthenticationForm.error_messages['invalid_login'] % {
-            'username': User._meta.get_field('username').verbose_name
-        })
-
-    def logout(self):
-        self.client.get('/logout/')
-
-    def test_password_change_fails_with_invalid_old_password(self):
-        self.login()
-        response = self.client.post('/password_change/', {
-            'old_password': 'donuts',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.assertFormError(response, PasswordChangeForm.error_messages['password_incorrect'])
-
-    def test_password_change_fails_with_mismatched_passwords(self):
-        self.login()
-        response = self.client.post('/password_change/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'donuts',
-        })
-        self.assertFormError(response, SetPasswordForm.error_messages['password_mismatch'])
-
-    def test_password_change_succeeds(self):
-        self.login()
-        self.client.post('/password_change/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.fail_login()
-        self.login(password='password1')
-
-    def test_password_change_done_succeeds(self):
-        self.login()
-        response = self.client.post('/password_change/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_change/done/')
-
-    @override_settings(LOGIN_URL='/login/')
-    def test_password_change_done_fails(self):
-        response = self.client.get('/password_change/done/')
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/login/?next=/password_change/done/')
-
-    def test_password_change_redirect_default(self):
-        self.login()
-        response = self.client.post('/password_change/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_change/done/')
-
-    def test_password_change_redirect_custom(self):
-        self.login()
-        response = self.client.post('/password_change/custom/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/custom/')
-
-    def test_password_change_redirect_custom_named(self):
-        self.login()
-        response = self.client.post('/password_change/custom/named/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        self.assertEqual(response.status_code, 302)
-        self.assertURLEqual(response.url, '/password_reset/')
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-class SessionAuthenticationTests(AuthViewsTestCase):
-    def test_user_password_change_updates_session(self):
-        """
-        #21649 - Ensure contrib.auth.views.password_change updates the user's
-        session auth hash after a password change so the session isn't logged out.
-        """
-        self.login()
-        response = self.client.post('/password_change/', {
-            'old_password': 'password',
-            'new_password1': 'password1',
-            'new_password2': 'password1',
-        })
-        # if the hash isn't updated, retrieving the redirection page will fail.
-        self.assertRedirects(response, '/password_change/done/')
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-class TestLogin(TestCase):
-    def setUp(self):
-        self.factory = RequestFactory()
-        self.request = self.factory.get('/')
-
-    def test_template_name(self):
-        response = login(self.request, 'template.html')
-        self.assertEqual(response.template_name, ['template.html'])
-
-    def test_form_class(self):
-        class NewForm(AuthenticationForm):
-            def confirm_login_allowed(self, user):
-                pass
-        response = login(self.request, 'template.html', None, NewForm)
-        self.assertEqual(response.context_data['form'].__class__.__name__, 'NewForm')
-
-    def test_extra_context(self):
-        extra_context = {'fake_context': 'fake_context'}
-        response = login(self.request, 'template.html', None, AuthenticationForm, extra_context)
-        self.assertEqual(response.resolve_context('fake_context'), 'fake_context')
-
-
-@ignore_warnings(category=RemovedInDjango21Warning)
-class TestLogout(AuthViewsTestCase):
-    def setUp(self):
-        self.login()
-        self.factory = RequestFactory()
-        self.request = self.factory.post('/')
-        self.request.session = self.client.session
-
-    def test_template_name(self):
-        response = logout(self.request, None, 'template.html')
-        self.assertEqual(response.template_name, ['template.html'])
-
-    def test_next_page(self):
-        response = logout(self.request, 'www.next_page.com')
-        self.assertEqual(response.url, 'www.next_page.com')
-
-    def test_extra_context(self):
-        extra_context = {'fake_context': 'fake_context'}
-        response = logout(self.request, None, 'template.html', None, extra_context)
-        self.assertEqual(response.resolve_context('fake_context'), 'fake_context')

+ 0 - 38
tests/auth_tests/urls_deprecated.py

@@ -1,38 +0,0 @@
-from django.conf.urls import url
-from django.contrib import admin
-from django.contrib.auth import views
-from django.contrib.auth.decorators import login_required
-
-# special urls for deprecated function-based views
-urlpatterns = [
-    url(r'^login/$', views.login, name='login'),
-    url(r'^logout/$', views.logout, name='logout'),
-    url(r'^password_change/$', views.password_change, name='password_change'),
-    url(r'^password_change/done/$', views.password_change_done, name='password_change_done'),
-    url(r'^password_reset/$', views.password_reset, name='password_reset'),
-    url(r'^password_reset/done/$', views.password_reset_done, name='password_reset_done'),
-    url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
-        views.password_reset_confirm, name='password_reset_confirm'),
-    url(r'^reset/done/$', views.password_reset_complete, name='password_reset_complete'),
-
-    url(r'^password_reset_from_email/$', views.password_reset, {'from_email': 'staffmember@example.com'}),
-    url(r'^password_reset_extra_email_context/$', views.password_reset,
-        {'extra_email_context': {'greeting': 'Hello!'}}),
-    url(r'^password_reset/custom_redirect/$', views.password_reset, {'post_reset_redirect': '/custom/'}),
-    url(r'^password_reset/custom_redirect/named/$', views.password_reset, {'post_reset_redirect': 'password_reset'}),
-    url(r'^password_reset/html_email_template/$', views.password_reset,
-        {'html_email_template_name': 'registration/html_password_reset_email.html'}),
-    url(r'^reset/custom/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
-        views.password_reset_confirm,
-        {'post_reset_redirect': '/custom/'}),
-    url(r'^reset/custom/named/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
-        views.password_reset_confirm,
-        {'post_reset_redirect': 'password_reset'}),
-    url(r'^password_change/custom/$', views.password_change, {'post_change_redirect': '/custom/'}),
-    url(r'^password_change/custom/named/$', views.password_change, {'post_change_redirect': 'password_reset'}),
-    url(r'^login_required/$', login_required(views.password_reset)),
-    url(r'^login_required_login_url/$', login_required(views.password_reset, login_url='/somewhere/')),
-
-    # This line is only required to render the password reset with is_admin=True
-    url(r'^admin/', admin.site.urls),
-]