Fixed #26823 -- Prevented update_last_login signal receiver from crashing if User model doesn't have last_login field.

This commit is contained in:
Linus Lewandowski 2017-05-27 14:35:02 +02:00 committed by Tim Graham
parent e84034b37a
commit eedc88bd4a
5 changed files with 38 additions and 5 deletions

View File

@ -3,8 +3,10 @@ from django.core import checks
from django.db.models.signals import post_migrate from django.db.models.signals import post_migrate
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from . import get_user_model
from .checks import check_models_permissions, check_user_model from .checks import check_models_permissions, check_user_model
from .management import create_permissions from .management import create_permissions
from .signals import user_logged_in
class AuthConfig(AppConfig): class AuthConfig(AppConfig):
@ -16,5 +18,8 @@ class AuthConfig(AppConfig):
create_permissions, create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions" dispatch_uid="django.contrib.auth.management.create_permissions"
) )
if hasattr(get_user_model(), 'last_login'):
from .models import update_last_login
user_logged_in.connect(update_last_login, dispatch_uid='update_last_login')
checks.register(check_user_model, checks.Tags.models) checks.register(check_user_model, checks.Tags.models)
checks.register(check_models_permissions, checks.Tags.models) checks.register(check_models_permissions, checks.Tags.models)

View File

@ -1,6 +1,5 @@
from django.contrib import auth from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.signals import user_logged_in
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail from django.core.mail import send_mail
@ -21,9 +20,6 @@ def update_last_login(sender, user, **kwargs):
user.save(update_fields=['last_login']) user.save(update_fields=['last_login'])
user_logged_in.connect(update_last_login)
class PermissionManager(models.Manager): class PermissionManager(models.Manager):
use_in_migrations = True use_in_migrations = True

View File

@ -4,6 +4,7 @@ from .custom_user import (
) )
from .invalid_models import CustomUserNonUniqueUsername from .invalid_models import CustomUserNonUniqueUsername
from .is_active import IsActiveTestUser1 from .is_active import IsActiveTestUser1
from .minimal import MinimalUser
from .uuid_pk import UUIDUser from .uuid_pk import UUIDUser
from .with_foreign_key import CustomUserWithFK, Email from .with_foreign_key import CustomUserWithFK, Email
from .with_integer_username import IntegerUsernameUser from .with_integer_username import IntegerUsernameUser
@ -11,5 +12,6 @@ from .with_integer_username import IntegerUsernameUser
__all__ = ( __all__ = (
'CustomUser', 'CustomUserWithoutIsActiveField', 'CustomPermissionsUser', 'CustomUser', 'CustomUserWithoutIsActiveField', 'CustomPermissionsUser',
'CustomUserWithFK', 'Email', 'ExtensionUser', 'IsActiveTestUser1', 'CustomUserWithFK', 'Email', 'ExtensionUser', 'IsActiveTestUser1',
'UUIDUser', 'CustomUserNonUniqueUsername', 'IntegerUsernameUser' 'MinimalUser', 'UUIDUser', 'CustomUserNonUniqueUsername',
'IntegerUsernameUser',
) )

View File

@ -0,0 +1,6 @@
from django.db import models
class MinimalUser(models.Model):
REQUIRED_FIELDS = ()
USERNAME_FIELD = 'id'

View File

@ -1,8 +1,12 @@
from django.apps import apps
from django.contrib.auth import authenticate, signals from django.contrib.auth import authenticate, signals
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import FieldDoesNotExist
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.test.client import RequestFactory from django.test.client import RequestFactory
from .models import MinimalUser
@override_settings(ROOT_URLCONF='auth_tests.urls') @override_settings(ROOT_URLCONF='auth_tests.urls')
class SignalTestCase(TestCase): class SignalTestCase(TestCase):
@ -82,3 +86,23 @@ class SignalTestCase(TestCase):
def test_failed_login_without_request(self): def test_failed_login_without_request(self):
authenticate(username='testclient', password='bad') authenticate(username='testclient', password='bad')
self.assertIsNone(self.login_failed[0]['request']) self.assertIsNone(self.login_failed[0]['request'])
def test_login_with_custom_user_without_last_login_field(self):
"""
The user_logged_in signal is only registered if the user model has a
last_login field.
"""
last_login_receivers = signals.user_logged_in.receivers
try:
signals.user_logged_in.receivers = []
with self.assertRaises(FieldDoesNotExist):
MinimalUser._meta.get_field('last_login')
with self.settings(AUTH_USER_MODEL='auth_tests.MinimalUser'):
apps.get_app_config('auth').ready()
self.assertEqual(signals.user_logged_in.receivers, [])
with self.settings(AUTH_USER_MODEL='auth.User'):
apps.get_app_config('auth').ready()
self.assertEqual(len(signals.user_logged_in.receivers), 1)
finally:
signals.user_logged_in.receivers = last_login_receivers