From 33242fe015a9519748cd328939dca3136f462344 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Wed, 24 Oct 2012 21:11:41 +0600 Subject: [PATCH] Fixed #19019 -- Fixed UserAdmin to log password change. Thanks Tuttle for the report. --- django/contrib/auth/admin.py | 2 + django/contrib/auth/forms.py | 8 +++ django/contrib/auth/tests/test_views.py | 66 +++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py index ca660606e5..e7cf8a3c64 100644 --- a/django/contrib/auth/admin.py +++ b/django/contrib/auth/admin.py @@ -127,6 +127,8 @@ class UserAdmin(admin.ModelAdmin): form = self.change_password_form(user, request.POST) if form.is_valid(): form.save() + change_message = self.construct_change_message(request, form, None) + self.log_change(request, request.user, change_message) msg = ugettext('Password changed successfully.') messages.success(request, msg) return HttpResponseRedirect('..') diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index 2d7a7c14d4..bf58d0e903 100644 --- a/django/contrib/auth/forms.py +++ b/django/contrib/auth/forms.py @@ -350,3 +350,11 @@ class AdminPasswordChangeForm(forms.Form): if commit: self.user.save() return self.user + + def _get_changed_data(self): + data = super(AdminPasswordChangeForm, self).changed_data + for name in self.fields.keys(): + if name not in data: + return [] + return ['password'] + changed_data = property(_get_changed_data) diff --git a/django/contrib/auth/tests/test_views.py b/django/contrib/auth/tests/test_views.py index b939dff058..a26d477aa0 100644 --- a/django/contrib/auth/tests/test_views.py +++ b/django/contrib/auth/tests/test_views.py @@ -8,6 +8,7 @@ except ImportError: # Python 2 from django.conf import global_settings, settings from django.contrib.sites.models import Site, RequestSite +from django.contrib.admin.models import LogEntry from django.contrib.auth.models import User from django.core import mail from django.core.urlresolvers import reverse, NoReverseMatch @@ -54,6 +55,11 @@ class AuthViewsTestCase(TestCase): self.assertTrue(SESSION_KEY in self.client.session) return response + def logout(self): + response = self.client.get('/admin/logout/') + self.assertEqual(response.status_code, 200) + self.assertTrue(SESSION_KEY not in 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())) @@ -670,18 +676,70 @@ class LogoutTest(AuthViewsTestCase): self.confirm_logged_out() @skipIfCustomUser +@override_settings( + PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), +) class ChangelistTests(AuthViewsTestCase): urls = 'django.contrib.auth.tests.urls_admin' + def setUp(self): + # Make me a superuser before logging in. + User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True) + self.login() + self.admin = User.objects.get(pk=1) + + def get_user_data(self, user): + return { + 'username': user.username, + 'password': user.password, + 'email': user.email, + 'is_active': user.is_active, + 'is_staff': user.is_staff, + 'is_superuser': user.is_superuser, + 'last_login_0': user.last_login.strftime('%Y-%m-%d'), + 'last_login_1': user.last_login.strftime('%H:%M:%S'), + 'initial-last_login_0': user.last_login.strftime('%Y-%m-%d'), + 'initial-last_login_1': user.last_login.strftime('%H:%M:%S'), + 'date_joined_0': user.date_joined.strftime('%Y-%m-%d'), + 'date_joined_1': user.date_joined.strftime('%H:%M:%S'), + 'initial-date_joined_0': user.date_joined.strftime('%Y-%m-%d'), + 'initial-date_joined_1': user.date_joined.strftime('%H:%M:%S'), + 'first_name': user.first_name, + 'last_name': user.last_name, + } + # #20078 - users shouldn't be allowed to guess password hashes via # repeated password__startswith queries. def test_changelist_disallows_password_lookups(self): - # Make me a superuser before loging in. - User.objects.filter(username='testclient').update(is_staff=True, is_superuser=True) - self.login() - # A lookup that tries to filter on password isn't OK with patch_logger('django.security.DisallowedModelAdminLookup', 'error') as logger_calls: response = self.client.get('/admin/auth/user/?password__startswith=sha1$') self.assertEqual(response.status_code, 400) self.assertEqual(len(logger_calls), 1) + + def test_user_change_email(self): + data = self.get_user_data(self.admin) + data['email'] = 'new_' + data['email'] + response = self.client.post('/admin/auth/user/%s/' % self.admin.pk, data) + self.assertRedirects(response, '/admin/auth/user/') + row = LogEntry.objects.latest('id') + self.assertEqual(row.change_message, 'Changed email.') + + def test_user_not_change(self): + response = self.client.post('/admin/auth/user/%s/' % self.admin.pk, + self.get_user_data(self.admin) + ) + self.assertRedirects(response, '/admin/auth/user/') + row = LogEntry.objects.latest('id') + self.assertEqual(row.change_message, 'No fields changed.') + + def test_user_change_password(self): + response = self.client.post('/admin/auth/user/%s/password/' % self.admin.pk, { + 'password1': 'password1', + 'password2': 'password1', + }) + self.assertRedirects(response, '/admin/auth/user/%s/' % self.admin.pk) + row = LogEntry.objects.latest('id') + self.assertEqual(row.change_message, 'Changed password.') + self.logout() + self.login(password='password1')