summaryrefslogtreecommitdiff
path: root/tests/auth_tests/test_signals.py
blob: b97377e2c94aa20d804bef6af3af4cc6d64e2c22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from django.apps import apps
from django.contrib.auth import authenticate, signals
from django.contrib.auth.models import User
from django.core.exceptions import FieldDoesNotExist
from django.test import TestCase, override_settings
from django.test.client import RequestFactory

from .models import MinimalUser, UserWithDisabledLastLoginField


@override_settings(ROOT_URLCONF="auth_tests.urls")
class SignalTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.u1 = User.objects.create_user(username="testclient", password="password")
        cls.u3 = User.objects.create_user(username="staff", password="password")

    def listener_login(self, user, **kwargs):
        self.logged_in.append(user)

    def listener_logout(self, user, **kwargs):
        self.logged_out.append(user)

    def listener_login_failed(self, sender, **kwargs):
        self.login_failed.append(kwargs)

    def setUp(self):
        """Set up the listeners and reset the logged in/logged out counters"""
        self.logged_in = []
        self.logged_out = []
        self.login_failed = []
        signals.user_logged_in.connect(self.listener_login)
        signals.user_logged_out.connect(self.listener_logout)
        signals.user_login_failed.connect(self.listener_login_failed)

    def tearDown(self):
        """Disconnect the listeners"""
        signals.user_logged_in.disconnect(self.listener_login)
        signals.user_logged_out.disconnect(self.listener_logout)
        signals.user_login_failed.disconnect(self.listener_login_failed)

    def test_login(self):
        # Only a successful login will trigger the success signal.
        self.client.login(username="testclient", password="bad")
        self.assertEqual(len(self.logged_in), 0)
        self.assertEqual(len(self.login_failed), 1)
        self.assertEqual(self.login_failed[0]["credentials"]["username"], "testclient")
        # verify the password is cleansed
        self.assertIn("***", self.login_failed[0]["credentials"]["password"])
        self.assertIn("request", self.login_failed[0])

        # Like this:
        self.client.login(username="testclient", password="password")
        self.assertEqual(len(self.logged_in), 1)
        self.assertEqual(self.logged_in[0].username, "testclient")

        # Ensure there were no more failures.
        self.assertEqual(len(self.login_failed), 1)

    def test_logout_anonymous(self):
        # The log_out function will still trigger the signal for anonymous
        # users.
        self.client.post("/logout/next_page/")
        self.assertEqual(len(self.logged_out), 1)
        self.assertIsNone(self.logged_out[0])

    def test_logout(self):
        self.client.login(username="testclient", password="password")
        self.client.post("/logout/next_page/")
        self.assertEqual(len(self.logged_out), 1)
        self.assertEqual(self.logged_out[0].username, "testclient")

    def test_update_last_login(self):
        """Only `last_login` is updated in `update_last_login`"""
        user = self.u3
        old_last_login = user.last_login

        user.username = "This username shouldn't get saved"
        request = RequestFactory().get("/login")
        signals.user_logged_in.send(sender=user.__class__, request=request, user=user)
        user = User.objects.get(pk=user.pk)
        self.assertEqual(user.username, "staff")
        self.assertNotEqual(user.last_login, old_last_login)

    def test_failed_login_without_request(self):
        authenticate(username="testclient", password="bad")
        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, [])

            # last_login is a property whose value is None.
            self.assertIsNone(UserWithDisabledLastLoginField().last_login)
            with self.settings(
                AUTH_USER_MODEL="auth_tests.UserWithDisabledLastLoginField"
            ):
                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