Fixed #25847 -- Made User.is_(anonymous|authenticated) properties.

This commit is contained in:
Jeremy Lainé 2016-04-02 13:18:26 +02:00 committed by Tim Graham
parent c16b9dd8e0
commit c1aec0feda
27 changed files with 238 additions and 93 deletions

View File

@ -345,6 +345,7 @@ answer newbie questions, and generally made Django that much better:
Jérémie Blaser <blaserje@gmail.com> Jérémie Blaser <blaserje@gmail.com>
Jeremy Carbaugh <jcarbaugh@gmail.com> Jeremy Carbaugh <jcarbaugh@gmail.com>
Jeremy Dunck <jdunck@gmail.com> Jeremy Dunck <jdunck@gmail.com>
Jeremy Lainé <jeremy.laine@m4x.org>
Jesse Young <adunar@gmail.com> Jesse Young <adunar@gmail.com>
jhenry <jhenry@theonion.com> jhenry <jhenry@theonion.com>
Jim Dalton <jim.dalton@gmail.com> Jim Dalton <jim.dalton@gmail.com>

View File

@ -138,7 +138,7 @@ def logout(request):
# Dispatch the signal before the user is logged out so the receivers have a # Dispatch the signal before the user is logged out so the receivers have a
# chance to find out *who* logged out. # chance to find out *who* logged out.
user = getattr(request, 'user', None) user = getattr(request, 'user', None)
if hasattr(user, 'is_authenticated') and not user.is_authenticated(): if hasattr(user, 'is_authenticated') and not user.is_authenticated:
user = None user = None
user_logged_out.send(sender=user.__class__, request=request, user=user) user_logged_out.send(sender=user.__class__, request=request, user=user)

View File

@ -45,7 +45,7 @@ class ModelBackend(object):
be either "group" or "user" to return permissions from be either "group" or "user" to return permissions from
`_get_group_permissions` or `_get_user_permissions` respectively. `_get_group_permissions` or `_get_user_permissions` respectively.
""" """
if not user_obj.is_active or user_obj.is_anonymous() or obj is not None: if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set() return set()
perm_cache_name = '_%s_perm_cache' % from_name perm_cache_name = '_%s_perm_cache' % from_name
@ -73,7 +73,7 @@ class ModelBackend(object):
return self._get_permissions(user_obj, obj, 'group') return self._get_permissions(user_obj, obj, 'group')
def get_all_permissions(self, user_obj, obj=None): def get_all_permissions(self, user_obj, obj=None):
if not user_obj.is_active or user_obj.is_anonymous() or obj is not None: if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set() return set()
if not hasattr(user_obj, '_perm_cache'): if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = self.get_user_permissions(user_obj) user_obj._perm_cache = self.get_user_permissions(user_obj)

View File

@ -10,6 +10,7 @@ from django.contrib.auth.hashers import (
) )
from django.db import models from django.db import models
from django.utils.crypto import get_random_string, salted_hmac from django.utils.crypto import get_random_string, salted_hmac
from django.utils.deprecation import CallableFalse, CallableTrue
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -79,19 +80,21 @@ class AbstractBaseUser(models.Model):
def natural_key(self): def natural_key(self):
return (self.get_username(),) return (self.get_username(),)
@property
def is_anonymous(self): def is_anonymous(self):
""" """
Always return False. This is a way of comparing User objects to Always return False. This is a way of comparing User objects to
anonymous users. anonymous users.
""" """
return False return CallableFalse
@property
def is_authenticated(self): def is_authenticated(self):
""" """
Always return True. This is a way to tell if the user has been Always return True. This is a way to tell if the user has been
authenticated in templates. authenticated in templates.
""" """
return True return CallableTrue
def set_password(self, raw_password): def set_password(self, raw_password):
self.password = make_password(raw_password) self.password = make_password(raw_password)

View File

@ -43,7 +43,7 @@ def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login
to the log-in page if necessary. to the log-in page if necessary.
""" """
actual_decorator = user_passes_test( actual_decorator = user_passes_test(
lambda u: u.is_authenticated(), lambda u: u.is_authenticated,
login_url=login_url, login_url=login_url,
redirect_field_name=redirect_field_name redirect_field_name=redirect_field_name
) )

View File

@ -70,13 +70,13 @@ class RemoteUserMiddleware(object):
# If specified header doesn't exist then remove any existing # If specified header doesn't exist then remove any existing
# authenticated remote-user, or return (leaving request.user set to # authenticated remote-user, or return (leaving request.user set to
# AnonymousUser by the AuthenticationMiddleware). # AnonymousUser by the AuthenticationMiddleware).
if self.force_logout_if_no_header and request.user.is_authenticated(): if self.force_logout_if_no_header and request.user.is_authenticated:
self._remove_invalid_user(request) self._remove_invalid_user(request)
return return
# If the user is already authenticated and that user is the user we are # If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already # getting passed in the headers, then the correct user is already
# persisted in the session and we don't need to continue. # persisted in the session and we don't need to continue.
if request.user.is_authenticated(): if request.user.is_authenticated:
if request.user.get_username() == self.clean_username(username, request): if request.user.get_username() == self.clean_username(username, request):
return return
else: else:

View File

@ -51,7 +51,7 @@ class LoginRequiredMixin(AccessMixin):
CBV mixin which verifies that the current user is authenticated. CBV mixin which verifies that the current user is authenticated.
""" """
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return self.handle_no_permission() return self.handle_no_permission()
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs) return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)

View File

@ -10,6 +10,7 @@ from django.core.mail import send_mail
from django.db import models from django.db import models
from django.db.models.manager import EmptyManager from django.db.models.manager import EmptyManager
from django.utils import six, timezone from django.utils import six, timezone
from django.utils.deprecation import CallableFalse, CallableTrue
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -438,11 +439,13 @@ class AnonymousUser(object):
def has_module_perms(self, module): def has_module_perms(self, module):
return _user_has_module_perms(self, module) return _user_has_module_perms(self, module)
@property
def is_anonymous(self): def is_anonymous(self):
return True return CallableTrue
@property
def is_authenticated(self): def is_authenticated(self):
return False return CallableFalse
def get_username(self): def get_username(self):
return self.username return self.username

View File

@ -68,7 +68,7 @@ def login(request, template_name='registration/login.html',
""" """
redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, '')) redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
if redirect_authenticated_user and request.user.is_authenticated(): if redirect_authenticated_user and request.user.is_authenticated:
redirect_to = _get_login_redirect_url(request, redirect_to) redirect_to = _get_login_redirect_url(request, redirect_to)
if redirect_to == request.path: if redirect_to == request.path:
raise ValueError( raise ValueError(

View File

@ -33,7 +33,7 @@ class FlatpageNode(template.Node):
# was provided, filter the list to only public flatpages. # was provided, filter the list to only public flatpages.
if self.user: if self.user:
user = self.user.resolve(context) user = self.user.resolve(context)
if not user.is_authenticated(): if not user.is_authenticated:
flatpages = flatpages.filter(registration_required=False) flatpages = flatpages.filter(registration_required=False)
else: else:
flatpages = flatpages.filter(registration_required=False) flatpages = flatpages.filter(registration_required=False)

View File

@ -52,7 +52,7 @@ def render_flatpage(request, f):
""" """
# If registration is required for accessing this page, and the user isn't # If registration is required for accessing this page, and the user isn't
# logged in, redirect to the login page. # logged in, redirect to the login page.
if f.registration_required and not request.user.is_authenticated(): if f.registration_required and not request.user.is_authenticated:
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
return redirect_to_login(request.path) return redirect_to_login(request.path)
if f.template_name: if f.template_name:

View File

@ -79,3 +79,33 @@ class DeprecationInstanceCheck(type):
self.deprecation_warning, 2 self.deprecation_warning, 2
) )
return super(DeprecationInstanceCheck, self).__instancecheck__(instance) return super(DeprecationInstanceCheck, self).__instancecheck__(instance)
class CallableBool:
"""
An boolean-like object that is also callable for backwards compatibility.
"""
do_not_call_in_templates = True
def __init__(self, value):
self.value = value
def __bool__(self):
return self.value
def __call__(self):
warnings.warn(
"Using user.is_authenticated() and user.is_anonymous() as a method "
"is deprecated. Remove the parentheses to use it as an attribute.",
RemovedInDjango20Warning, stacklevel=2
)
return self.value
def __nonzero__(self): # Python 2 compatibility
return self.value
def __repr__(self):
return 'CallableBool(%r)' % self.value
CallableFalse = CallableBool(False)
CallableTrue = CallableBool(True)

View File

@ -256,7 +256,7 @@ given view by setting the ``HttpRequest``s ``exception_reporter_filter``
attribute:: attribute::
def my_view(request): def my_view(request):
if request.user.is_authenticated(): if request.user.is_authenticated:
request.exception_reporter_filter = CustomExceptionReporterFilter() request.exception_reporter_filter = CustomExceptionReporterFilter()
... ...

View File

@ -147,6 +147,9 @@ details on these changes.
* The shim for supporting custom related manager classes without a * The shim for supporting custom related manager classes without a
``_apply_rel_filters()`` method will be removed. ``_apply_rel_filters()`` method will be removed.
* Using ``User.is_authenticated()`` and ``User.is_anonymous()`` as methods
will no longer be supported.
.. _deprecation-removed-in-1.10: .. _deprecation-removed-in-1.10:
1.10 1.10

View File

@ -111,6 +111,41 @@ Fields
A datetime designating when the account was created. Is set to the A datetime designating when the account was created. Is set to the
current date/time by default when the account is created. current date/time by default when the account is created.
Attributes
----------
.. class:: models.User
.. attribute:: is_authenticated
Read-only attribute which is always ``True`` (as opposed to
``AnonymousUser.is_authenticated`` which is always ``False``). This is
a way to tell if the user has been authenticated. This does not imply
any permissions and doesn't check if the user is active or has a valid
session. Even though normally you will check this attribute on
``request.user`` to find out whether it has been populated by the
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
(representing the currently logged-in user), you should know this
attribute is ``True`` for any :class:`~models.User` instance.
.. versionchanged:: 1.10
In older versions, this was a method. Backwards-compatibility
support for using it as a method will be removed in Django 2.0.
.. attribute:: is_anonymous
Read-only attribute which is always ``False``. This is a way of
differentiating :class:`~models.User` and :class:`~models.AnonymousUser`
objects. Generally, you should prefer using
:attr:`~django.contrib.auth.models.User.is_authenticated` to this
attribute.
.. versionchanged:: 1.10
In older versions, this was a method. Backwards-compatibility
support for using it as a method will be removed in Django 2.0.
Methods Methods
------- -------
@ -122,28 +157,6 @@ Methods
out, you should use this method instead of referencing the username out, you should use this method instead of referencing the username
attribute directly. attribute directly.
.. method:: is_anonymous()
Always returns ``False``. This is a way of differentiating
:class:`~django.contrib.auth.models.User` and
:class:`~django.contrib.auth.models.AnonymousUser` objects.
Generally, you should prefer using
:meth:`~django.contrib.auth.models.User.is_authenticated()` to this
method.
.. method:: is_authenticated()
Always returns ``True`` (as opposed to
``AnonymousUser.is_authenticated()`` which always returns ``False``).
This is a way to tell if the user has been authenticated. This does not
imply any permissions, and doesn't check if the user is active or has
a valid session. Even though normally you will call this method on
``request.user`` to find out whether it has been populated by the
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
(representing the currently logged-in user), you should know this method
returns ``True`` for any :class:`~django.contrib.auth.models.User`
instance.
.. method:: get_full_name() .. method:: get_full_name()
Returns the :attr:`~django.contrib.auth.models.User.first_name` plus Returns the :attr:`~django.contrib.auth.models.User.first_name` plus
@ -287,6 +300,10 @@ Manager methods
string. string.
* :meth:`~django.contrib.auth.models.User.get_username()` always returns * :meth:`~django.contrib.auth.models.User.get_username()` always returns
the empty string. the empty string.
* :attr:`~django.contrib.auth.models.User.is_anonymous` is ``True``
instead of ``False``.
* :attr:`~django.contrib.auth.models.User.is_authenticated` is
``False`` instead of ``True``.
* :attr:`~django.contrib.auth.models.User.is_staff` and * :attr:`~django.contrib.auth.models.User.is_staff` and
:attr:`~django.contrib.auth.models.User.is_superuser` are always :attr:`~django.contrib.auth.models.User.is_superuser` are always
``False``. ``False``.
@ -294,10 +311,6 @@ Manager methods
* :attr:`~django.contrib.auth.models.User.groups` and * :attr:`~django.contrib.auth.models.User.groups` and
:attr:`~django.contrib.auth.models.User.user_permissions` are always :attr:`~django.contrib.auth.models.User.user_permissions` are always
empty. empty.
* :meth:`~django.contrib.auth.models.User.is_anonymous()` returns ``True``
instead of ``False``.
* :meth:`~django.contrib.auth.models.User.is_authenticated()` returns
``False`` instead of ``True``.
* :meth:`~django.contrib.auth.models.User.set_password()`, * :meth:`~django.contrib.auth.models.User.set_password()`,
:meth:`~django.contrib.auth.models.User.check_password()`, :meth:`~django.contrib.auth.models.User.check_password()`,
:meth:`~django.db.models.Model.save` and :meth:`~django.db.models.Model.save` and
@ -471,21 +484,21 @@ The following backends are available in :mod:`django.contrib.auth.backends`:
Returns the set of permission strings the ``user_obj`` has from their Returns the set of permission strings the ``user_obj`` has from their
own user permissions. Returns an empty set if own user permissions. Returns an empty set if
:meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``. :attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: get_group_permissions(user_obj, obj=None) .. method:: get_group_permissions(user_obj, obj=None)
Returns the set of permission strings the ``user_obj`` has from the Returns the set of permission strings the ``user_obj`` has from the
permissions of the groups they belong. Returns an empty set if permissions of the groups they belong. Returns an empty set if
:meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``. :attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: get_all_permissions(user_obj, obj=None) .. method:: get_all_permissions(user_obj, obj=None)
Returns the set of permission strings the ``user_obj`` has, including both Returns the set of permission strings the ``user_obj`` has, including both
user permissions and group permissions. Returns an empty set if user permissions and group permissions. Returns an empty set if
:meth:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or :attr:`~django.contrib.auth.models.AbstractBaseUser.is_anonymous` or
:attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``. :attr:`~django.contrib.auth.models.CustomUser.is_active` is ``False``.
.. method:: has_perm(user_obj, perm, obj=None) .. method:: has_perm(user_obj, perm, obj=None)

View File

@ -233,9 +233,9 @@ middleware class is listed in :setting:`MIDDLEWARE_CLASSES`.
logged-in user. If the user isn't currently logged in, ``user`` will be set logged-in user. If the user isn't currently logged in, ``user`` will be set
to an instance of :class:`~django.contrib.auth.models.AnonymousUser`. You to an instance of :class:`~django.contrib.auth.models.AnonymousUser`. You
can tell them apart with can tell them apart with
:meth:`~django.contrib.auth.models.User.is_authenticated`, like so:: :attr:`~django.contrib.auth.models.User.is_authenticated`, like so::
if request.user.is_authenticated(): if request.user.is_authenticated:
... # Do something for logged-in users. ... # Do something for logged-in users.
else: else:
... # Do something for anonymous users. ... # Do something for anonymous users.

View File

@ -730,6 +730,10 @@ Miscellaneous
* Middleware classes are now initialized when the server starts rather than * Middleware classes are now initialized when the server starts rather than
during the first request. during the first request.
* If you override ``is_authenticated()`` or ``is_anonymous()`` in a custom user
model, you must convert them to attributes or properties as described in
:ref:`the deprecation note <user-is-auth-anon-deprecation>`.
.. _deprecated-features-1.10: .. _deprecated-features-1.10:
Features deprecated in 1.10 Features deprecated in 1.10
@ -857,6 +861,37 @@ features, is deprecated. Replace it with a custom lookup::
models.CharField.register_lookup(Search) models.CharField.register_lookup(Search)
models.TextField.register_lookup(Search) models.TextField.register_lookup(Search)
.. _user-is-auth-anon-deprecation:
Using ``User.is_authenticated()`` and ``User.is_anonymous()`` as methods
------------------------------------------------------------------------
The ``is_authenticated()`` and ``is_anonymous()`` methods of
:class:`~django.contrib.auth.models.AbstractBaseUser` and
:class:`~django.contrib.auth.models.AnonymousUser` classes are now
properties. They will still work as methods until Django 2.0, but all usage
in Django now uses attribute access.
For example, if you use
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware` and want
to know whether the user is currently logged-in you would use::
if request.user.is_authenticated:
... # Do something for logged-in users.
else:
... # Do something for anonymous users.
instead of ``request.user.is_authenticated()``.
This change avoids accidental information leakage if you forget to call the
method, e.g.::
if request.user.is_authenticated:
return sensitive_information
If you override these methods in a custom user model, you must change them to
properties or attributes.
Custom manager classes available through ``prefetch_related`` must define a ``_apply_rel_filters()`` method Custom manager classes available through ``prefetch_related`` must define a ``_apply_rel_filters()`` method
----------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------

View File

@ -603,7 +603,7 @@ password resets. You must then provide some key implementation details:
raised a deprecation warning in older versions and is no longer raised a deprecation warning in older versions and is no longer
supported in Django 1.9). supported in Django 1.9).
The following methods are available on any subclass of The following attributes and methods are available on any subclass of
:class:`~django.contrib.auth.models.AbstractBaseUser`: :class:`~django.contrib.auth.models.AbstractBaseUser`:
.. class:: models.AbstractBaseUser .. class:: models.AbstractBaseUser
@ -612,20 +612,34 @@ The following methods are available on any subclass of
Returns the value of the field nominated by ``USERNAME_FIELD``. Returns the value of the field nominated by ``USERNAME_FIELD``.
.. method:: models.AbstractBaseUser.is_anonymous() .. attribute:: models.AbstractBaseUser.is_authenticated
Always returns ``False``. This is a way of differentiating Read-only attribute which is always ``True`` (as opposed to
from :class:`~django.contrib.auth.models.AnonymousUser` objects. ``AnonymousUser.is_authenticated`` which is always ``False``).
Generally, you should prefer using This is a way to tell if the user has been authenticated. This does not
:meth:`~django.contrib.auth.models.AbstractBaseUser.is_authenticated()` to this imply any permissions and doesn't check if the user is active or has
method. a valid session. Even though normally you will check this attribute on
``request.user`` to find out whether it has been populated by the
:class:`~django.contrib.auth.middleware.AuthenticationMiddleware`
(representing the currently logged-in user), you should know this
attribute is ``True`` for any :class:`~models.User` instance.
.. method:: models.AbstractBaseUser.is_authenticated() .. versionchanged:: 1.10
Always returns ``True``. This is a way to tell if the user has been In older versions, this was a method. Backwards-compatibility
authenticated. This does not imply any permissions, and doesn't check support for using it as a method will be removed in Django 2.0.
if the user is active - it only indicates that the user has provided a
valid username and password. .. attribute:: models.AbstractBaseUser.is_anonymous
Read-only attribute which is always ``False``. This is a way of
differentiating :class:`~models.User` and :class:`~models.AnonymousUser`
objects. Generally, you should prefer using
:attr:`~models.User.is_authenticated` to this attribute.
.. versionchanged:: 1.10
In older versions, this was a method. Backwards-compatibility
support for using it as a method will be removed in Django 2.0.
.. method:: models.AbstractBaseUser.set_password(raw_password) .. method:: models.AbstractBaseUser.set_password(raw_password)

View File

@ -306,9 +306,9 @@ of :class:`~django.contrib.auth.models.AnonymousUser`, otherwise it will be an
instance of :class:`~django.contrib.auth.models.User`. instance of :class:`~django.contrib.auth.models.User`.
You can tell them apart with You can tell them apart with
:meth:`~django.contrib.auth.models.User.is_authenticated()`, like so:: :attr:`~django.contrib.auth.models.User.is_authenticated`, like so::
if request.user.is_authenticated(): if request.user.is_authenticated:
# Do something for authenticated users. # Do something for authenticated users.
... ...
else: else:
@ -421,15 +421,15 @@ The raw way
~~~~~~~~~~~ ~~~~~~~~~~~
The simple, raw way to limit access to pages is to check The simple, raw way to limit access to pages is to check
:meth:`request.user.is_authenticated() :attr:`request.user.is_authenticated
<django.contrib.auth.models.User.is_authenticated()>` and either redirect to a <django.contrib.auth.models.User.is_authenticated>` and either redirect to a
login page:: login page::
from django.conf import settings from django.conf import settings
from django.shortcuts import redirect from django.shortcuts import redirect
def my_view(request): def my_view(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path)) return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
# ... # ...
@ -438,7 +438,7 @@ login page::
from django.shortcuts import render from django.shortcuts import render
def my_view(request): def my_view(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return render(request, 'myapp/login_error.html') return render(request, 'myapp/login_error.html')
# ... # ...

View File

@ -1170,7 +1170,7 @@ decorator)::
@vary_on_cookie @vary_on_cookie
def list_blog_entries_view(request): def list_blog_entries_view(request):
if request.user.is_anonymous(): if request.user.is_anonymous:
response = render_only_public_entries() response = render_only_public_entries()
patch_cache_control(response, public=True) patch_cache_control(response, public=True)
else: else:

View File

@ -235,7 +235,7 @@ We'll demonstrate this with the ``Author`` model we used in the
model = Author model = Author
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return HttpResponseForbidden() return HttpResponseForbidden()
# Look up the author we're interested in. # Look up the author we're interested in.
@ -466,7 +466,7 @@ Our new ``AuthorDetail`` looks like this::
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return HttpResponseForbidden() return HttpResponseForbidden()
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
@ -552,7 +552,7 @@ template as ``AuthorDisplay`` is using on ``GET``::
model = Author model = Author
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
return HttpResponseForbidden() return HttpResponseForbidden()
self.object = self.get_object() self.object = self.get_object()
return super(AuthorInterest, self).post(request, *args, **kwargs) return super(AuthorInterest, self).post(request, *args, **kwargs)

View File

@ -12,7 +12,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http import HttpRequest from django.http import HttpRequest
from django.test import ( from django.test import (
SimpleTestCase, TestCase, modify_settings, override_settings, SimpleTestCase, TestCase, mock, modify_settings, override_settings,
) )
from .models import ( from .models import (
@ -142,8 +142,7 @@ class BaseModelBackendTest(object):
self.assertEqual(backend.get_user_permissions(user), {'auth.test_user', 'auth.test_group'}) self.assertEqual(backend.get_user_permissions(user), {'auth.test_user', 'auth.test_group'})
self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'}) self.assertEqual(backend.get_group_permissions(user), {'auth.test_group'})
user.is_anonymous = lambda: True with mock.patch.object(self.UserModel, 'is_anonymous', True):
self.assertEqual(backend.get_all_permissions(user), set()) self.assertEqual(backend.get_all_permissions(user), set())
self.assertEqual(backend.get_user_permissions(user), set()) self.assertEqual(backend.get_user_permissions(user), set())
self.assertEqual(backend.get_group_permissions(user), set()) self.assertEqual(backend.get_group_permissions(user), set())
@ -334,14 +333,14 @@ class SimpleRowlevelBackend(object):
if isinstance(obj, TestObj): if isinstance(obj, TestObj):
if user.username == 'test2': if user.username == 'test2':
return True return True
elif user.is_anonymous() and perm == 'anon': elif user.is_anonymous and perm == 'anon':
return True return True
elif not user.is_active and perm == 'inactive': elif not user.is_active and perm == 'inactive':
return True return True
return False return False
def has_module_perms(self, user, app_label): def has_module_perms(self, user, app_label):
if not user.is_anonymous() and not user.is_active: if not user.is_anonymous and not user.is_active:
return False return False
return app_label == "app1" return app_label == "app1"
@ -352,7 +351,7 @@ class SimpleRowlevelBackend(object):
if not isinstance(obj, TestObj): if not isinstance(obj, TestObj):
return ['none'] return ['none']
if user.is_anonymous(): if user.is_anonymous:
return ['anon'] return ['anon']
if user.username == 'test2': if user.username == 'test2':
return ['simple', 'advanced'] return ['simple', 'advanced']
@ -578,7 +577,7 @@ class ChangedBackendSettingsTest(TestCase):
# Assert that the user retrieval is successful and the user is # Assert that the user retrieval is successful and the user is
# anonymous as the backend is not longer available. # anonymous as the backend is not longer available.
self.assertIsNotNone(user) self.assertIsNotNone(user)
self.assertTrue(user.is_anonymous()) self.assertTrue(user.is_anonymous)
class TypeErrorBackend(object): class TypeErrorBackend(object):

View File

@ -1,5 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import warnings
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User from django.contrib.auth.models import AnonymousUser, User
@ -44,7 +46,8 @@ class BasicTestCase(TestCase):
self.assertEqual(u.get_username(), 'testuser') self.assertEqual(u.get_username(), 'testuser')
# Check authentication/permissions # Check authentication/permissions
self.assertTrue(u.is_authenticated()) self.assertFalse(u.is_anonymous)
self.assertTrue(u.is_authenticated)
self.assertFalse(u.is_staff) self.assertFalse(u.is_staff)
self.assertTrue(u.is_active) self.assertTrue(u.is_active)
self.assertFalse(u.is_superuser) self.assertFalse(u.is_superuser)
@ -53,6 +56,26 @@ class BasicTestCase(TestCase):
u2 = User.objects.create_user('testuser2', 'test2@example.com') u2 = User.objects.create_user('testuser2', 'test2@example.com')
self.assertFalse(u2.has_usable_password()) self.assertFalse(u2.has_usable_password())
def test_is_anonymous_authenticated_method_deprecation(self):
deprecation_message = (
'Using user.is_authenticated() and user.is_anonymous() as a '
'method is deprecated. Remove the parentheses to use it as an '
'attribute.'
)
u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
# Backwards-compatibility callables
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
self.assertFalse(u.is_anonymous())
self.assertEqual(len(warns), 1)
self.assertEqual(str(warns[0].message), deprecation_message)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always')
self.assertTrue(u.is_authenticated())
self.assertEqual(len(warns), 1)
self.assertEqual(str(warns[0].message), deprecation_message)
def test_user_no_email(self): def test_user_no_email(self):
"Check that users can be created without an email" "Check that users can be created without an email"
u = User.objects.create_user('testuser1') u = User.objects.create_user('testuser1')
@ -70,13 +93,34 @@ class BasicTestCase(TestCase):
self.assertEqual(a.pk, None) self.assertEqual(a.pk, None)
self.assertEqual(a.username, '') self.assertEqual(a.username, '')
self.assertEqual(a.get_username(), '') self.assertEqual(a.get_username(), '')
self.assertFalse(a.is_authenticated()) self.assertTrue(a.is_anonymous)
self.assertFalse(a.is_authenticated)
self.assertFalse(a.is_staff) self.assertFalse(a.is_staff)
self.assertFalse(a.is_active) self.assertFalse(a.is_active)
self.assertFalse(a.is_superuser) self.assertFalse(a.is_superuser)
self.assertEqual(a.groups.all().count(), 0) self.assertEqual(a.groups.all().count(), 0)
self.assertEqual(a.user_permissions.all().count(), 0) self.assertEqual(a.user_permissions.all().count(), 0)
def test_anonymous_user_is_anonymous_authenticated_method_deprecation(self):
a = AnonymousUser()
deprecation_message = (
'Using user.is_authenticated() and user.is_anonymous() as a '
'method is deprecated. Remove the parentheses to use it as an '
'attribute.'
)
# Backwards-compatibility callables
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always') # prevent warnings from appearing as errors
self.assertTrue(a.is_anonymous())
self.assertEqual(len(warns), 1)
self.assertEqual(str(warns[0].message), deprecation_message)
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter('always') # prevent warnings from appearing as errors
self.assertFalse(a.is_authenticated())
self.assertEqual(len(warns), 1)
self.assertEqual(str(warns[0].message), deprecation_message)
def test_superuser(self): def test_superuser(self):
"Check the creation and properties of a superuser" "Check the creation and properties of a superuser"
super = User.objects.create_superuser('super', 'super@example.com', 'super') super = User.objects.create_superuser('super', 'super@example.com', 'super')

View File

@ -16,7 +16,7 @@ class TestAuthenticationMiddleware(TestCase):
self.request.session = self.client.session self.request.session = self.client.session
self.middleware.process_request(self.request) self.middleware.process_request(self.request)
self.assertIsNotNone(self.request.user) self.assertIsNotNone(self.request.user)
self.assertFalse(self.request.user.is_anonymous()) self.assertFalse(self.request.user.is_anonymous)
def test_changed_password_invalidates_session(self): def test_changed_password_invalidates_session(self):
# After password change, user should be anonymous # After password change, user should be anonymous
@ -24,6 +24,6 @@ class TestAuthenticationMiddleware(TestCase):
self.user.save() self.user.save()
self.middleware.process_request(self.request) self.middleware.process_request(self.request)
self.assertIsNotNone(self.request.user) self.assertIsNotNone(self.request.user)
self.assertTrue(self.request.user.is_anonymous()) self.assertTrue(self.request.user.is_anonymous)
# session should be flushed # session should be flushed
self.assertIsNone(self.request.session.session_key) self.assertIsNone(self.request.session.session_key)

View File

@ -5,7 +5,7 @@ from django.contrib.auth.mixins import (
from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.http import HttpResponse from django.http import HttpResponse
from django.test import RequestFactory, TestCase from django.test import RequestFactory, TestCase, mock
from django.views.generic import View from django.views.generic import View
@ -78,9 +78,9 @@ class AccessMixinTests(TestCase):
with self.assertRaises(PermissionDenied): with self.assertRaises(PermissionDenied):
view(request) view(request)
@mock.patch.object(models.User, 'is_authenticated', False)
def test_stacked_mixins_not_logged_in(self): def test_stacked_mixins_not_logged_in(self):
user = models.User.objects.create(username='joe', password='qwerty') user = models.User.objects.create(username='joe', password='qwerty')
user.is_authenticated = lambda: False
perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser')) perms = models.Permission.objects.filter(codename__in=('add_customuser', 'change_customuser'))
user.user_permissions.add(*perms) user.user_permissions.add(*perms)
request = self.factory.get('/rand') request = self.factory.get('/rand')

View File

@ -38,15 +38,15 @@ class RemoteUserTest(TestCase):
num_users = User.objects.count() num_users = User.objects.count()
response = self.client.get('/remote_user/') response = self.client.get('/remote_user/')
self.assertTrue(response.context['user'].is_anonymous()) self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users) self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', **{self.header: None}) response = self.client.get('/remote_user/', **{self.header: None})
self.assertTrue(response.context['user'].is_anonymous()) self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users) self.assertEqual(User.objects.count(), num_users)
response = self.client.get('/remote_user/', **{self.header: ''}) response = self.client.get('/remote_user/', **{self.header: ''})
self.assertTrue(response.context['user'].is_anonymous()) self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users) self.assertEqual(User.objects.count(), num_users)
def test_unknown_user(self): def test_unknown_user(self):
@ -118,7 +118,7 @@ class RemoteUserTest(TestCase):
self.assertEqual(response.context['user'].username, 'knownuser') self.assertEqual(response.context['user'].username, 'knownuser')
# During the session, the REMOTE_USER header disappears. Should trigger logout. # During the session, the REMOTE_USER header disappears. Should trigger logout.
response = self.client.get('/remote_user/') response = self.client.get('/remote_user/')
self.assertEqual(response.context['user'].is_anonymous(), True) self.assertTrue(response.context['user'].is_anonymous)
# verify the remoteuser middleware will not remove a user # verify the remoteuser middleware will not remove a user
# authenticated via another backend # authenticated via another backend
User.objects.create_user(username='modeluser', password='foo') User.objects.create_user(username='modeluser', password='foo')
@ -148,7 +148,7 @@ class RemoteUserTest(TestCase):
def test_inactive_user(self): def test_inactive_user(self):
User.objects.create(username='knownuser', is_active=False) User.objects.create(username='knownuser', is_active=False)
response = self.client.get('/remote_user/', **{self.header: 'knownuser'}) response = self.client.get('/remote_user/', **{self.header: 'knownuser'})
self.assertTrue(response.context['user'].is_anonymous()) self.assertTrue(response.context['user'].is_anonymous)
class RemoteUserNoCreateBackend(RemoteUserBackend): class RemoteUserNoCreateBackend(RemoteUserBackend):
@ -167,7 +167,7 @@ class RemoteUserNoCreateTest(RemoteUserTest):
def test_unknown_user(self): def test_unknown_user(self):
num_users = User.objects.count() num_users = User.objects.count()
response = self.client.get('/remote_user/', **{self.header: 'newuser'}) response = self.client.get('/remote_user/', **{self.header: 'newuser'})
self.assertTrue(response.context['user'].is_anonymous()) self.assertTrue(response.context['user'].is_anonymous)
self.assertEqual(User.objects.count(), num_users) self.assertEqual(User.objects.count(), num_users)
@ -268,5 +268,5 @@ class PersistentRemoteUserTest(RemoteUserTest):
self.assertEqual(response.context['user'].username, 'knownuser') self.assertEqual(response.context['user'].username, 'knownuser')
# Should stay logged in if the REMOTE_USER header disappears. # Should stay logged in if the REMOTE_USER header disappears.
response = self.client.get('/remote_user/') response = self.client.get('/remote_user/')
self.assertEqual(response.context['user'].is_anonymous(), False) self.assertFalse(response.context['user'].is_anonymous)
self.assertEqual(response.context['user'].username, 'knownuser') self.assertEqual(response.context['user'].username, 'knownuser')

View File

@ -45,7 +45,7 @@ class LazyRedirectView(RedirectView):
url = reverse_lazy('named-lazy-url-redirected-to') url = reverse_lazy('named-lazy-url-redirected-to')
@user_passes_test(lambda u: u.is_authenticated(), login_url=reverse_lazy('some-login-page')) @user_passes_test(lambda u: u.is_authenticated, login_url=reverse_lazy('some-login-page'))
def login_required_view(request): def login_required_view(request):
return HttpResponse('Hello you') return HttpResponse('Hello you')