1import unittest 2 3import pytz 4 5from django.conf import settings 6from django.contrib.auth import get_user_model 7from django.contrib.auth import views as auth_views 8from django.contrib.auth.models import Group, Permission 9from django.contrib.auth.tokens import PasswordResetTokenGenerator 10from django.core import mail 11from django.core.files.uploadedfile import SimpleUploadedFile 12from django.test import RequestFactory, TestCase, override_settings 13from django.urls import reverse 14 15from wagtail.admin.localization import ( 16 WAGTAILADMIN_PROVIDED_LANGUAGES, get_available_admin_languages, get_available_admin_time_zones) 17from wagtail.admin.views.account import account, profile_tab 18from wagtail.images.tests.utils import get_test_image_file 19from wagtail.tests.utils import WagtailTestUtils 20from wagtail.users.models import UserProfile 21 22 23class TestAuthentication(TestCase, WagtailTestUtils): 24 """ 25 This tests that users can login and logout of the admin interface 26 """ 27 def test_login_view(self): 28 """ 29 This tests that the login view responds with a login page 30 """ 31 # Get login page 32 response = self.client.get(reverse('wagtailadmin_login')) 33 34 # Check that the user received a login page 35 self.assertEqual(response.status_code, 200) 36 self.assertTemplateUsed(response, 'wagtailadmin/login.html') 37 38 def test_login_view_post(self): 39 """ 40 This posts user credentials to the login view and checks that 41 the user was logged in successfully 42 """ 43 # Create user to log in with 44 self.create_superuser(username='test', email='test@email.com', password='password') 45 46 # Post credentials to the login page 47 response = self.client.post(reverse('wagtailadmin_login'), { 48 'username': 'test@email.com' if settings.AUTH_USER_MODEL == 'emailuser.EmailUser' else 'test', 49 'password': 'password', 50 51 # NOTE: This is set using a hidden field in reality 52 'next': reverse('wagtailadmin_home'), 53 }) 54 55 # Check that the user was redirected to the dashboard 56 self.assertRedirects(response, reverse('wagtailadmin_home')) 57 58 # Check that the user was logged in 59 self.assertTrue('_auth_user_id' in self.client.session) 60 self.assertEqual( 61 str(self.client.session['_auth_user_id']), 62 str(get_user_model().objects.get(email='test@email.com').pk) 63 ) 64 65 def test_already_logged_in_redirect(self): 66 """ 67 This tests that a user who is already logged in is automatically 68 redirected to the admin dashboard if they try to access the login 69 page 70 """ 71 # Login 72 self.login() 73 74 # Get login page 75 response = self.client.get(reverse('wagtailadmin_login')) 76 77 # Check that the user was redirected to the dashboard 78 self.assertRedirects(response, reverse('wagtailadmin_home')) 79 80 def test_logged_in_as_non_privileged_user_doesnt_redirect(self): 81 """ 82 This tests that if the user is logged in but hasn't got permission 83 to access the admin, they are not redirected to the admin 84 85 This tests issue #431 86 """ 87 # Login as unprivileged user 88 self.create_user(username='unprivileged', password='123') 89 self.login(username='unprivileged', password='123') 90 91 # Get login page 92 response = self.client.get(reverse('wagtailadmin_login')) 93 94 # Check that the user received a login page and was not redirected 95 self.assertEqual(response.status_code, 200) 96 self.assertTemplateUsed(response, 'wagtailadmin/login.html') 97 98 def test_logout(self): 99 """ 100 This tests that the user can logout 101 """ 102 # Login 103 self.login() 104 105 # Get logout page 106 response = self.client.get(reverse('wagtailadmin_logout')) 107 108 # Check that the user was redirected to the login page 109 self.assertRedirects(response, reverse('wagtailadmin_login')) 110 111 # Check that the user was logged out 112 self.assertFalse('_auth_user_id' in self.client.session) 113 114 def test_not_logged_in_redirect(self): 115 """ 116 This tests that a not logged in user is redirected to the 117 login page 118 """ 119 # Get dashboard 120 response = self.client.get(reverse('wagtailadmin_home')) 121 122 # Check that the user was redirected to the login page and that next was set correctly 123 self.assertRedirects(response, reverse('wagtailadmin_login') + '?next=' + reverse('wagtailadmin_home')) 124 125 def test_not_logged_in_gives_403_to_ajax_requests(self): 126 """ 127 This tests that a not logged in user is given a 403 error on AJAX requests 128 """ 129 # Get dashboard 130 response = self.client.get(reverse('wagtailadmin_home'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') 131 132 # AJAX requests should be given a 403 error instead of being redirected 133 self.assertEqual(response.status_code, 403) 134 135 def test_not_logged_in_redirect_default_settings(self): 136 """ 137 This does the same as the above test but checks that it 138 redirects to the correct place when the user has not set 139 the LOGIN_URL setting correctly 140 """ 141 # Get dashboard with default LOGIN_URL setting 142 with self.settings(LOGIN_URL='django.contrib.auth.views.login'): 143 response = self.client.get(reverse('wagtailadmin_home')) 144 145 # Check that the user was redirected to the login page and that next was set correctly 146 # Note: The user will be redirected to 'django.contrib.auth.views.login' but 147 # this must be the same URL as 'wagtailadmin_login' 148 self.assertEqual(response.status_code, 302) 149 self.assertRedirects(response, reverse('wagtailadmin_login') + '?next=' + reverse('wagtailadmin_home')) 150 151 def test_logged_in_no_permission_redirect(self): 152 """ 153 This tests that a logged in user without admin access permissions is 154 redirected to the login page, with an error message 155 """ 156 # Login as unprivileged user 157 self.create_user(username='unprivileged', password='123') 158 self.login(username='unprivileged', password='123') 159 160 # Get dashboard 161 response = self.client.get(reverse('wagtailadmin_home'), follow=True) 162 163 # Check that the user was redirected to the login page and that next was set correctly 164 self.assertRedirects(response, reverse('wagtailadmin_login') + '?next=' + reverse('wagtailadmin_home')) 165 self.assertContains(response, 'You do not have permission to access the admin') 166 167 def test_logged_in_no_permission_gives_403_to_ajax_requests(self): 168 """ 169 This tests that a logged in user without admin access permissions is 170 given a 403 error on ajax requests 171 """ 172 # Login as unprivileged user 173 self.create_user(username='unprivileged', password='123') 174 self.login(username='unprivileged', password='123') 175 176 # Get dashboard 177 response = self.client.get(reverse('wagtailadmin_home'), HTTP_X_REQUESTED_WITH='XMLHttpRequest') 178 179 # AJAX requests should be given a 403 error instead of being redirected 180 self.assertEqual(response.status_code, 403) 181 182 183class TestAccountSectionUtilsMixin: 184 def assertPanelActive(self, response, name): 185 panels = set() 186 for panelset in response.context['panels_by_tab'].values(): 187 for panel in panelset: 188 panels.add(panel.name) 189 self.assertIn(name, panels, "Panel %s not active in response" % name) 190 191 def assertPanelNotActive(self, response, name): 192 panels = set() 193 for panelset in response.context['panels_by_tab'].values(): 194 for panel in panelset: 195 panels.add(panel.name) 196 self.assertNotIn(name, panels, "Panel %s active in response" % name) 197 198 def post_form(self, extra_post_data): 199 post_data = { 200 'name_email-first_name': 'Test', 201 'name_email-last_name': 'User', 202 'name_email-email': self.user.email, 203 'notifications-submitted_notifications': 'false', 204 'notifications-approved_notifications': 'false', 205 'notifications-rejected_notifications': 'true', 206 'notifications-updated_comments_notifications': 'true', 207 'locale-preferred_language': 'es', 208 'locale-current_time_zone': 'Europe/London', 209 } 210 post_data.update(extra_post_data) 211 return self.client.post(reverse('wagtailadmin_account'), post_data) 212 213 214class TestAccountSection(TestCase, WagtailTestUtils, TestAccountSectionUtilsMixin): 215 """ 216 This tests that the accounts section is working 217 """ 218 def setUp(self): 219 self.user = self.login() 220 221 def test_account_view(self): 222 """ 223 This tests that the accounts view responds with an index page 224 """ 225 # Get account page 226 response = self.client.get(reverse('wagtailadmin_account')) 227 228 # Check that the user received an account page 229 self.assertEqual(response.status_code, 200) 230 self.assertTemplateUsed(response, 'wagtailadmin/account/account.html') 231 232 self.assertPanelActive(response, 'name_email') 233 self.assertPanelActive(response, 'notifications') 234 self.assertPanelActive(response, 'locale') 235 self.assertPanelActive(response, 'password') 236 237 # These fields may hide themselves 238 self.assertContains(response, "Email:") 239 self.assertContains(response, "Preferred language:") 240 241 if settings.USE_TZ: 242 self.assertContains(response, "Current time zone:") 243 else: 244 self.assertNotContains(response, "Current time zone:") 245 246 # Form media should be included on the page 247 self.assertContains(response, 'vendor/colorpicker.js') 248 249 def test_change_name_post(self): 250 response = self.post_form({ 251 'name_email-first_name': 'Fox', 252 'name_email-last_name': 'Mulder', 253 }) 254 255 # Check that the user was redirected to the account page 256 self.assertRedirects(response, reverse('wagtailadmin_account')) 257 258 # Check that the name was changed 259 self.user.refresh_from_db() 260 self.assertEqual(self.user.first_name, 'Fox') 261 self.assertEqual(self.user.last_name, 'Mulder') 262 263 def test_change_email_post(self): 264 response = self.post_form({ 265 'name_email-email': 'test@email.com', 266 }) 267 268 # Check that the user was redirected to the account page 269 self.assertRedirects(response, reverse('wagtailadmin_account')) 270 271 # Check that the email was changed 272 self.user.refresh_from_db() 273 self.assertEqual(self.user.email, 'test@email.com') 274 275 def test_change_email_not_valid(self): 276 response = self.post_form({ 277 'name_email-email': 'test@email', 278 }) 279 280 # Check that the user wasn't redirected 281 self.assertEqual(response.status_code, 200) 282 283 # Check that a validation error was raised 284 self.assertTrue('email' in response.context['panels_by_tab'][profile_tab][0].get_form().errors.keys()) 285 286 # Check that the email was not changed 287 self.user.refresh_from_db() 288 self.assertNotEqual(self.user.email, 'test@email') 289 290 @override_settings(WAGTAIL_EMAIL_MANAGEMENT_ENABLED=False) 291 def test_with_email_management_disabled(self): 292 # Get account page 293 response = self.client.get(reverse('wagtailadmin_account')) 294 295 self.assertEqual(response.status_code, 200) 296 self.assertTemplateUsed(response, 'wagtailadmin/account/account.html') 297 self.assertNotContains(response, "Email:") 298 299 @override_settings(WAGTAIL_PASSWORD_MANAGEMENT_ENABLED=False) 300 def test_account_view_with_password_management_disabled(self): 301 # Get account page 302 response = self.client.get(reverse('wagtailadmin_account')) 303 304 self.assertEqual(response.status_code, 200) 305 self.assertTemplateUsed(response, 'wagtailadmin/account/account.html') 306 # Page should NOT contain a 'Change password' option 307 self.assertNotContains(response, "Change password") 308 309 @override_settings(WAGTAIL_PASSWORD_MANAGEMENT_ENABLED=False) 310 def test_change_password_view_disabled(self): 311 response = self.client.get(reverse('wagtailadmin_account')) 312 self.assertPanelNotActive(response, 'password') 313 314 def test_change_password(self): 315 response = self.post_form({ 316 'password-old_password': 'password', 317 'password-new_password1': 'newpassword', 318 'password-new_password2': 'newpassword', 319 }) 320 321 # Check that the user was redirected to the account page 322 self.assertRedirects(response, reverse('wagtailadmin_account')) 323 324 # Check that the password was changed 325 self.user.refresh_from_db() 326 self.assertTrue(self.user.check_password('newpassword')) 327 328 def test_change_password_post_password_mismatch(self): 329 response = self.post_form({ 330 'password-old_password': 'password', 331 'password-new_password1': 'newpassword', 332 'password-new_password2': 'badpassword', 333 }) 334 335 # Check that the user wasn't redirected 336 self.assertEqual(response.status_code, 200) 337 338 # Find password panel through context 339 password_panel = None 340 for panelset in response.context['panels_by_tab'].values(): 341 for panel in panelset: 342 if panel.name == 'password': 343 password_panel = panel 344 break 345 346 # Check that a validation error was raised 347 password_form = password_panel.get_form() 348 self.assertTrue('new_password2' in password_form.errors.keys()) 349 self.assertTrue("The two password fields didn’t match." in password_form.errors['new_password2']) 350 351 # Check that the password was not changed 352 self.user.refresh_from_db() 353 self.assertTrue(self.user.check_password('password')) 354 355 def test_change_notifications(self): 356 response = self.post_form({ 357 'submitted_notifications': 'false', 358 'approved_notifications': 'false', 359 'rejected_notifications': 'true', 360 'updated_comments_notifications': 'true', 361 }) 362 363 # Check that the user was redirected to the account page 364 self.assertRedirects(response, reverse('wagtailadmin_account')) 365 366 profile = UserProfile.get_for_user(get_user_model().objects.get(pk=self.user.pk)) 367 368 # Check that the notification preferences are as submitted 369 self.assertFalse(profile.submitted_notifications) 370 self.assertFalse(profile.approved_notifications) 371 self.assertTrue(profile.rejected_notifications) 372 self.assertTrue(profile.updated_comments_notifications) 373 374 def test_change_language_preferences(self): 375 response = self.post_form({ 376 'locale-preferred_language': 'es', 377 }) 378 379 # Check that the user was redirected to the account page 380 self.assertRedirects(response, reverse('wagtailadmin_account')) 381 382 profile = UserProfile.get_for_user(self.user) 383 profile.refresh_from_db() 384 385 # Check that the language preferences are stored 386 self.assertEqual(profile.preferred_language, 'es') 387 388 # check that the updated language preference is now indicated in HTML header 389 response = self.client.get(reverse('wagtailadmin_home')) 390 self.assertContains(response, '<html class="no-js" lang="es" dir="ltr">') 391 392 def test_unset_language_preferences(self): 393 profile = UserProfile.get_for_user(self.user) 394 profile.preferred_language = 'en' 395 profile.save() 396 397 response = self.post_form({ 398 'locale-preferred_language': '', 399 }) 400 401 # Check that the user was redirected to the account page 402 self.assertRedirects(response, reverse('wagtailadmin_account')) 403 404 # Check that the language preferences are stored 405 profile.refresh_from_db() 406 self.assertEqual(profile.preferred_language, '') 407 408 # Check that the current language is assumed as English 409 self.assertEqual(profile.get_preferred_language(), "en") 410 411 @override_settings(WAGTAILADMIN_PERMITTED_LANGUAGES=[('en', 'English'), ('es', 'Spanish')]) 412 def test_available_admin_languages_with_permitted_languages(self): 413 self.assertListEqual(get_available_admin_languages(), [('en', 'English'), ('es', 'Spanish')]) 414 415 def test_available_admin_languages_by_default(self): 416 self.assertListEqual(get_available_admin_languages(), WAGTAILADMIN_PROVIDED_LANGUAGES) 417 418 @override_settings(WAGTAILADMIN_PERMITTED_LANGUAGES=[('en', 'English')]) 419 def test_not_show_options_if_only_one_language_is_permitted(self): 420 response = self.client.get(reverse('wagtailadmin_account')) 421 self.assertNotContains(response, "Preferred language:") 422 423 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 424 def test_change_current_time_zone(self): 425 response = self.post_form({ 426 'locale-current_time_zone': 'Pacific/Fiji', 427 }) 428 429 # Check that the user was redirected to the account page 430 self.assertRedirects(response, reverse('wagtailadmin_account')) 431 432 profile = UserProfile.get_for_user(self.user) 433 profile.refresh_from_db() 434 435 # Check that the current time zone is stored 436 self.assertEqual(profile.current_time_zone, 'Pacific/Fiji') 437 438 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 439 def test_unset_current_time_zone(self): 440 response = self.post_form({ 441 'locale-current_time_zone': '', 442 }) 443 444 # Check that the user was redirected to the account page 445 self.assertRedirects(response, reverse('wagtailadmin_account')) 446 447 profile = UserProfile.get_for_user(self.user) 448 profile.refresh_from_db() 449 450 # Check that the current time zone are stored 451 self.assertEqual(profile.current_time_zone, '') 452 453 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 454 @override_settings(WAGTAIL_USER_TIME_ZONES=['Africa/Addis_Ababa', 'America/Argentina/Buenos_Aires']) 455 def test_available_admin_time_zones_with_permitted_time_zones(self): 456 self.assertListEqual(get_available_admin_time_zones(), 457 ['Africa/Addis_Ababa', 'America/Argentina/Buenos_Aires']) 458 459 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 460 def test_available_admin_time_zones_by_default(self): 461 self.assertListEqual(get_available_admin_time_zones(), pytz.common_timezones) 462 463 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 464 @override_settings(WAGTAIL_USER_TIME_ZONES=['Europe/London']) 465 def test_not_show_options_if_only_one_time_zone_is_permitted(self): 466 response = self.client.get(reverse('wagtailadmin_account')) 467 self.assertNotContains(response, "Current time zone:") 468 469 @unittest.skipIf(settings.USE_TZ, "Timezone support is enabled") 470 def test_not_show_options_if_timezone_support_disabled(self): 471 response = self.client.get(reverse('wagtailadmin_account')) 472 self.assertNotContains(response, "Current time zone:") 473 474 @unittest.skipUnless(settings.USE_TZ, "Timezone support is disabled") 475 @override_settings( 476 WAGTAIL_USER_TIME_ZONES=['Europe/London'], 477 WAGTAILADMIN_PERMITTED_LANGUAGES=[('en', 'English')] 478 ) 479 def test_doesnt_render_locale_panel_when_only_one_timezone_and_one_locale_permitted(self): 480 response = self.client.get(reverse('wagtailadmin_account')) 481 self.assertPanelNotActive(response, 'locale') 482 483 def test_sensitive_post_parameters(self): 484 request = RequestFactory().post('wagtailadmin_account', data={}) 485 request.user = self.user 486 account(request) 487 self.assertTrue(hasattr(request, 'sensitive_post_parameters')) 488 self.assertEqual(request.sensitive_post_parameters, '__ALL__') 489 490 491class TestAccountUploadAvatar(TestCase, WagtailTestUtils, TestAccountSectionUtilsMixin): 492 def setUp(self): 493 self.user = self.login() 494 self.avatar = get_test_image_file() 495 self.other_avatar = get_test_image_file() 496 497 def test_account_view(self): 498 """ 499 This tests that the account view renders a "Upload a profile picture:" field 500 """ 501 response = self.client.get(reverse('wagtailadmin_account')) 502 503 self.assertEqual(response.status_code, 200) 504 self.assertContains(response, "Upload a profile picture:") 505 506 def test_set_custom_avatar_stores_and_get_custom_avatar(self): 507 response = self.post_form({ 508 'avatar-avatar': SimpleUploadedFile('other.png', self.other_avatar.file.getvalue()) 509 }) 510 # Check that the user was redirected to the account page 511 self.assertRedirects(response, reverse('wagtailadmin_account')) 512 513 profile = UserProfile.get_for_user(self.user) 514 profile.refresh_from_db() 515 self.assertIn('other.png', profile.avatar.url) 516 517 def test_user_upload_another_image_removes_previous_one(self): 518 profile = UserProfile.get_for_user(self.user) 519 profile.avatar = self.avatar 520 profile.save() 521 522 old_avatar_path = profile.avatar.path 523 524 # Upload a new avatar 525 response = self.post_form({ 526 'avatar-avatar': SimpleUploadedFile('other.png', self.other_avatar.file.getvalue()) 527 }) 528 # Check that the user was redirected to the account page 529 self.assertRedirects(response, reverse('wagtailadmin_account')) 530 531 # Check the avatar was changed 532 profile.refresh_from_db() 533 self.assertIn('other.png', profile.avatar.url) 534 535 # Check old avatar doesn't exist anymore in filesystem 536 with self.assertRaises(FileNotFoundError): 537 open(old_avatar_path) 538 539 def test_no_value_preserves_current_avatar(self): 540 """ 541 Tests that submitting a blank value for avatar doesn't remove it. 542 """ 543 profile = UserProfile.get_for_user(self.user) 544 profile.avatar = self.avatar 545 profile.save() 546 547 # Upload a new avatar 548 response = self.post_form({}) 549 # Check that the user was redirected to the account page 550 self.assertRedirects(response, reverse('wagtailadmin_account')) 551 552 # Check the avatar was changed 553 profile.refresh_from_db() 554 self.assertIn('test.png', profile.avatar.url) 555 556 def test_clear_removes_current_avatar(self): 557 """ 558 Tests that submitting a blank value for avatar doesn't remove it. 559 """ 560 profile = UserProfile.get_for_user(self.user) 561 profile.avatar = self.avatar 562 profile.save() 563 564 # Upload a new avatar 565 response = self.post_form({ 566 'avatar-clear': 'on' 567 }) 568 # Check that the user was redirected to the account page 569 self.assertRedirects(response, reverse('wagtailadmin_account')) 570 571 # Check the avatar was changed 572 profile.refresh_from_db() 573 self.assertIn('test.png', profile.avatar.url) 574 575 576class TestAccountManagementForNonModerator(TestCase, WagtailTestUtils): 577 """ 578 Tests of reduced-functionality for editors 579 """ 580 def setUp(self): 581 # Create a non-moderator user 582 self.submitter = self.create_user('submitter', 'submitter@example.com', 'password') 583 self.submitter.groups.add(Group.objects.get(name='Editors')) 584 585 self.login(username='submitter', password='password') 586 587 def test_notification_preferences_panel_reduced_for_non_moderators(self): 588 """ 589 This tests that a user without publish permissions is not shown the 590 notification preference for 'submitted' items 591 """ 592 response = self.client.get(reverse('wagtailadmin_account')) 593 594 # Find notifications panel through context 595 notifications_panel = None 596 for panelset in response.context['panels_by_tab'].values(): 597 for panel in panelset: 598 if panel.name == 'notifications': 599 notifications_panel = panel 600 break 601 602 notifications_form = notifications_panel.get_form() 603 self.assertIn('approved_notifications', notifications_form.fields.keys()) 604 self.assertIn('rejected_notifications', notifications_form.fields.keys()) 605 self.assertNotIn('submitted_notifications', notifications_form.fields.keys()) 606 self.assertIn('updated_comments_notifications', notifications_form.fields.keys()) 607 608 609class TestAccountManagementForAdminOnlyUser(TestCase, WagtailTestUtils, TestAccountSectionUtilsMixin): 610 """ 611 Tests for users with no edit/publish permissions at all 612 """ 613 def setUp(self): 614 # Create a non-moderator user 615 admin_only_group = Group.objects.create(name='Admin Only') 616 admin_only_group.permissions.add(Permission.objects.get(codename='access_admin')) 617 618 self.admin_only_user = self.create_user( 619 'admin_only_user', 620 'admin_only_user@example.com', 621 'password' 622 ) 623 self.admin_only_user.groups.add(admin_only_group) 624 625 self.login(username='admin_only_user', password='password') 626 627 def test_notification_preferences_not_rendered_for_admin_only_users(self): 628 """ 629 Test that the user is not shown the notification preferences panel 630 """ 631 response = self.client.get(reverse('wagtailadmin_account')) 632 self.assertPanelNotActive(response, 'notifications') 633 634 635class TestPasswordReset(TestCase, WagtailTestUtils): 636 """ 637 This tests that the password reset is working 638 """ 639 def setUp(self): 640 # Create a user 641 self.create_superuser(username='test', email='test@email.com', password='password') 642 643 def test_password_reset_view(self): 644 """ 645 This tests that the password reset view returns a password reset page 646 """ 647 # Get password reset page 648 response = self.client.get(reverse('wagtailadmin_password_reset')) 649 650 # Check that the user received a password reset page 651 self.assertEqual(response.status_code, 200) 652 self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/form.html') 653 654 def test_password_reset_view_post(self): 655 """ 656 This posts an email address to the password reset view and 657 checks that a password reset email was sent 658 """ 659 # Post email address to password reset view 660 post_data = { 661 'email': 'test@email.com', 662 } 663 response = self.client.post(reverse('wagtailadmin_password_reset'), post_data) 664 665 # Check that the user was redirected to the done page 666 self.assertRedirects(response, reverse('wagtailadmin_password_reset_done')) 667 668 # Check that a password reset email was sent to the user 669 self.assertEqual(len(mail.outbox), 1) 670 self.assertEqual(mail.outbox[0].to, ['test@email.com']) 671 self.assertEqual(mail.outbox[0].subject, "Password reset") 672 673 def test_password_reset_view_post_unknown_email(self): 674 """ 675 This posts an unknown email address to the password reset view and 676 checks that the password reset form raises a validation error 677 """ 678 post_data = { 679 'email': 'unknown@email.com', 680 } 681 response = self.client.post(reverse('wagtailadmin_password_reset'), post_data) 682 683 # Check that the user was redirected to the done page 684 self.assertRedirects(response, 685 reverse('wagtailadmin_password_reset_done')) 686 687 # Check that an email was not sent 688 self.assertEqual(len(mail.outbox), 0) 689 690 def test_password_reset_view_post_invalid_email(self): 691 """ 692 This posts an incalid email address to the password reset view and 693 checks that the password reset form raises a validation error 694 """ 695 post_data = { 696 'email': 'Hello world!', 697 } 698 response = self.client.post(reverse('wagtailadmin_password_reset'), post_data) 699 700 # Check that the user wasn't redirected 701 self.assertEqual(response.status_code, 200) 702 703 # Check that a validation error was raised 704 self.assertTrue('email' in response.context['form'].errors.keys()) 705 self.assertTrue("Enter a valid email address." in response.context['form'].errors['email']) 706 707 # Check that an email was not sent 708 self.assertEqual(len(mail.outbox), 0) 709 710 def setup_password_reset_confirm_tests(self): 711 from django.utils.encoding import force_bytes, force_str 712 from django.utils.http import urlsafe_base64_encode 713 714 # Get user 715 self.user = get_user_model().objects.get(email='test@email.com') 716 717 # Generate a password reset token 718 self.password_reset_token = PasswordResetTokenGenerator().make_token(self.user) 719 720 # Generate a password reset uid 721 self.password_reset_uid = force_str(urlsafe_base64_encode(force_bytes(self.user.pk))) 722 723 # Create url_args 724 token = auth_views.PasswordResetConfirmView.reset_url_token 725 726 self.url_kwargs = dict(uidb64=self.password_reset_uid, token=token) 727 728 # Add token to session object 729 s = self.client.session 730 s.update({ 731 auth_views.INTERNAL_RESET_SESSION_TOKEN: self.password_reset_token, 732 }) 733 s.save() 734 735 def test_password_reset_confirm_view_invalid_link(self): 736 """ 737 This tests that the password reset view shows an error message if the link is invalid 738 """ 739 self.setup_password_reset_confirm_tests() 740 741 # Create invalid url_args 742 self.url_kwargs = dict(uidb64=self.password_reset_uid, token="invalid-token") 743 744 # Get password reset confirm page 745 response = self.client.get(reverse('wagtailadmin_password_reset_confirm', kwargs=self.url_kwargs)) 746 747 # Check that the user received a password confirm done page 748 self.assertEqual(response.status_code, 200) 749 self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/confirm.html') 750 self.assertFalse(response.context['validlink']) 751 self.assertContains(response, 'The password reset link was invalid, possibly because it has already been used.') 752 self.assertContains(response, 'Request a new password reset') 753 754 def test_password_reset_confirm_view(self): 755 """ 756 This tests that the password reset confirm view returns a password reset confirm page 757 """ 758 self.setup_password_reset_confirm_tests() 759 760 # Get password reset confirm page 761 response = self.client.get(reverse('wagtailadmin_password_reset_confirm', kwargs=self.url_kwargs)) 762 763 # Check that the user received a password confirm done page 764 self.assertEqual(response.status_code, 200) 765 self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/confirm.html') 766 767 def test_password_reset_confirm_view_post(self): 768 """ 769 This posts a new password to the password reset confirm view and checks 770 that the users password was changed 771 """ 772 self.setup_password_reset_confirm_tests() 773 774 # Post new password to change password page 775 post_data = { 776 'new_password1': 'newpassword', 777 'new_password2': 'newpassword', 778 } 779 response = self.client.post(reverse('wagtailadmin_password_reset_confirm', kwargs=self.url_kwargs), post_data) 780 781 # Check that the user was redirected to the complete page 782 self.assertRedirects(response, reverse('wagtailadmin_password_reset_complete')) 783 784 # Check that the password was changed 785 self.assertTrue(get_user_model().objects.get(email='test@email.com').check_password('newpassword')) 786 787 def test_password_reset_confirm_view_post_password_mismatch(self): 788 """ 789 This posts a two passwords that don't match to the password reset 790 confirm view and checks that a validation error was raised 791 """ 792 self.setup_password_reset_confirm_tests() 793 794 # Post new password to change password page 795 post_data = { 796 'new_password1': 'newpassword', 797 'new_password2': 'badpassword', 798 } 799 response = self.client.post(reverse('wagtailadmin_password_reset_confirm', kwargs=self.url_kwargs), post_data) 800 801 # Check that the user wasn't redirected 802 self.assertEqual(response.status_code, 200) 803 804 # Check that a validation error was raised 805 self.assertTrue('new_password2' in response.context['form'].errors.keys()) 806 self.assertTrue("The two password fields didn’t match." in response.context['form'].errors['new_password2']) 807 808 # Check that the password was not changed 809 self.assertTrue(get_user_model().objects.get(email='test@email.com').check_password('password')) 810 811 def test_password_reset_done_view(self): 812 """ 813 This tests that the password reset done view returns a password reset done page 814 """ 815 # Get password reset done page 816 response = self.client.get(reverse('wagtailadmin_password_reset_done')) 817 818 # Check that the user received a password reset done page 819 self.assertEqual(response.status_code, 200) 820 self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/done.html') 821 822 def test_password_reset_complete_view(self): 823 """ 824 This tests that the password reset complete view returns a password reset complete page 825 """ 826 # Get password reset complete page 827 response = self.client.get(reverse('wagtailadmin_password_reset_complete')) 828 829 # Check that the user received a password reset complete page 830 self.assertEqual(response.status_code, 200) 831 self.assertTemplateUsed(response, 'wagtailadmin/account/password_reset/complete.html') 832