class TestLocalUserResetPassword(SecureClientMixin, TestCase, MultitenantSiteTestingMixin): @classmethod def setUpTestData(cls): cls.set_up_test_sites_and_users() cls.wagtail_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'wagtail.flint.oursites.com', }) def setUp(self): super(TestLocalUserResetPassword, self).setUp() # Logger dummy. self.logger_dummy = Dummy(warning=Dummy(), info=Dummy()) # Form dummy for view tests. self.form_dummy().is_valid() returns False by default. self.form_dummy = Dummy(default_return=Dummy( is_valid=Dummy(default_return=False), save=Dummy(default_return=Dummy())), ) # Messages dummy. self.messages_dummy = Dummy(error=Dummy(), success=Dummy(), button=Dummy()) ############ # TESTS ############ def test_form_save(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = {'username': '******', 'email': '*****@*****.**'} # Create the form with valid POST data. form = LocalUserAdminResetPasswordForm(form_data) # Confirm that the given form data passes validation. self.assertTrue(form.is_valid()) # Save the form, and confirm that it sends the email properly. mail_dummy = Dummy() with Replacer() as r: r.replace( 'wagtail_patches.forms.LocalUserAdminResetPasswordForm.send_mail', mail_dummy) with LogCapture() as capture: form.save(request) self.assertEqual(len(mail_dummy.calls), 1) self.assertEqual(mail_dummy.calls[0]['args'][2]['user'].username, form_data['username']) self.assertEqual(mail_dummy.calls[0]['args'][2]['domain'], request.site.hostname) self.assertEqual(mail_dummy.calls[0]['kwargs']['from_email'], settings.SERVER_EMAIL) self.assertTrue('user.local.password_reset.admin' in str(capture)) def test_local_user_reset_password_view_GET(self): self.login('wagtail_admin') with Replacer() as r: r.replace( 'wagtail_patches.views.users.LocalUserAdminResetPasswordForm', self.form_dummy) r.replace('wagtail.wagtailadmin.messages', self.messages_dummy) # First, test to make sure the routing works. response = self.client.get(reverse( 'wagtailusers_users:admin_reset_password', args=[self.wagtail_editor.username]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed( response, 'wagtail_patches/users/admin_reset_password.html') self.form_dummy.reset_dummy() # Confirm that unpriviledged users can't access the view. self.login('wagtail_editor') response = self.client.get(reverse( 'wagtailusers_users:admin_reset_password', args=[self.wagtail_editor.username]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailadmin_home')) self.assertEqual(len(self.messages_dummy.error.calls), 1) # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site response = admin_reset_password(request, self.wagtail_editor.username) self.assertEqual( response.template_name, 'wagtail_patches/users/admin_reset_password.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) def test_local_user_reset_password_view_POST_invalid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False. r.replace( 'wagtail_patches.views.users.LocalUserAdminResetPasswordForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # First, test to make sure the routing works. form_data = { 'username': '******', 'email': '*****@*****.**' } response = self.client.post(reverse( 'wagtailusers_users:admin_reset_password', args=[self.wagtail_editor.username]), data=form_data, HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed( response, 'wagtail_patches/users/admin_reset_password.html') self.form_dummy.reset_dummy() self.messages_dummy.reset_dummy() # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.post( reverse('wagtailusers_users:admin_reset_password', args=[self.wagtail_editor.username]), data=form_data, HTTP_HOST=self.wagtail_site.hostname) request.user = self.wagtail_admin request.site = self.wagtail_site response = admin_reset_password(request, self.wagtail_editor.username) self.assertEqual( response.template_name, 'wagtail_patches/users/admin_reset_password.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request.POST) def test_edit_local_user_view_POST_valid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False, so we need to change that for the valid data test. self.form_dummy.default_return.is_valid = Dummy( default_return=True) r.replace( 'wagtail_patches.views.users.LocalUserAdminResetPasswordForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) form_data = { 'username': '******', 'email': '*****@*****.**' } request = self.wagtail_factory.post(reverse( 'wagtailusers_users:admin_reset_password', args=[self.wagtail_editor.username]), data=form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = admin_reset_password(request, self.wagtail_editor.username) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailusers_users:index')) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request.POST) # form.is_valid() returned true, so the user should have been saved and been informed about success. self.assertEqual(len(self.form_dummy.default_return.save.calls), 1) self.assertEqual(len(self.messages_dummy.success.calls), 1) self.assertEqual(self.messages_dummy.success.calls[0]['args'][0], request)
class TestLDAPUserCreate(SecureClientMixin, TestCase, MultitenantSiteTestingMixin): @classmethod def setUpTestData(cls): cls.set_up_test_sites_and_users() cls.wagtail_factory = RequestFactory(**{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'wagtail.flint.oursites.com', }) cls.test_factory = RequestFactory(**{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'test.flint.oursites.com', }) def setUp(self): super(TestLDAPUserCreate, self).setUp() # Dummies out search_ldap_for_user(), returning good data. self.mock_email = '*****@*****.**' self.mock_sn = 'Shmoe' self.mock_given_name = 'Joe' self.ldap_search_dummy = Dummy( default_return=('fake_dn', { 'givenName': [self.mock_given_name], 'sn': [self.mock_sn], 'CAPPrimaryEmail': [self.mock_email], }) ) # Dummy for when search_ldap_for_user() should fail. self.ldap_search_dummy_error = lambda x: thrower(ldap.LDAPError('error!')) # NOQA # Dummy for when search_ldap_for_user() should return no results. self.ldap_search_dummy_no_results = Dummy(default_return=None) # Form dummy, for view tests self.form_dummy = Dummy( default_return=Dummy( is_valid=Dummy( default_return=False ), save=Dummy( default_return=Dummy( set_unusable_password=Dummy() ) ) ), ) # Messages dummy self.messages_dummy = Dummy(error=Dummy(), success=Dummy(), button=Dummy()) ############ # TESTS ############ def test_create_brand_new_LDAP_nonsuperuser_happy_path(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') # Should create a new user named "wagtail_user1" in the "wagtail.flint.oursites.com Admins" Group. form_data = {'username': '******', 'groups': [self.wagtail_admins_group.pk]} # Confirm that this user doesn't already exist, so we know this is the "brand new user" workflow. self.assertFalse(get_user_model().objects.filter(username=form_data['username']).exists()) with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Create the form and make the sure the initialization worked as exepected. form = LDAPUserCreateForm(request, form_data) # Non-superusers cannot set a new user as a superuser. self.assertNotIn('is_superuser', form.fields) # Non-superusers only see the Groups' shortnames. self.assertEqual( form.fields['groups'].choices, [(self.wagtail_admins_group.pk, 'Admins'), (self.wagtail_editors_group.pk, 'Editors')] ) self.assertTrue(form.fields['groups'].required) self.assertEqual(form.fields['groups'].error_messages['required'], form.error_messages['group_required']) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) self.assertEqual(len(self.ldap_search_dummy.calls), 1) # Save the user to the database, which would populate it from LDAP, if it weren't being mocked to fail. r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy_error) with LogCapture() as capture: new_user = form.save() # ldap lookup fails because the mock is configured to fail self.assertTrue('user.update_from_ldap.failed' in str(capture)) # Ensure we still created the user self.assertTrue('user.ldap.create' in str(capture)) self.assertEqual(new_user.username, form_data['username']) # LDAP is mocked to error out. The population of these should have gracefully failed, leaving them unset. self.assertEqual(new_user.email, '') self.assertEqual(new_user.first_name, '') self.assertEqual(new_user.last_name, '') self.assertTrue(user_is_member_of_site(new_user, request.site)) self.assertEqual(list(new_user.groups.all()), [self.wagtail_admins_group]) def test_create_brand_new_LDAP_superuser_happy_path(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') # Should create a new user named "superuser1" who is a superuser. form_data = {'username': '******', 'is_superuser': True} # Confirm that this user doesn't already exist, so we know this is the "brand new user" workflow. self.assertFalse(get_user_model().objects.filter(username=form_data['username']).exists()) with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Create the form and make the sure the initialization worked as exepected. form = LDAPUserCreateForm(request, form_data) # Superusers CAN set a new user as a superuser. self.assertIn('is_superuser', form.fields) # Superusers see all Groups' full names. self.assertSequenceEqual( # Need to convert to a list here, since it's currently a ModelChoiceIterator. list(form.fields['groups'].choices), [(g.pk, g.name) for g in Group.objects.all()] ) self.assertFalse(form.fields['groups'].required) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) self.assertEqual(len(self.ldap_search_dummy.calls), 1) # Save the user to the database, which would populate it from LDAP, if it weren't being mocked out. with LogCapture() as capture: new_user = form.save() self.assertEqual(len(self.ldap_search_dummy.calls), 2) # ldap lookup self.assertTrue('user.update_from_ldap.success' in str(capture)) # user creation self.assertTrue('user.ldap.create' in str(capture)) self.assertEqual(new_user.username, form_data['username']) self.assertTrue(new_user.is_superuser) self.assertEqual(new_user.email, self.mock_email) self.assertEqual(new_user.first_name, self.mock_given_name) self.assertEqual(new_user.last_name, self.mock_sn) self.assertFalse(user_is_member_of_site(new_user, request.site)) def test_superuser_has_to_assign_LDAP_user_to_group_or_set_superuser(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') # Should fail to create a user since no group or superuser flag is set. form_data = {'username': '******'} # Confirm that this user doesn't already exist, so we know this is the "brand new user" workflow. self.assertFalse(get_user_model().objects.filter(username=form_data['username']).exists()) with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Form_data doesn't set a Group or the is_superuser flag, so the form should throw an error. form = LDAPUserCreateForm(request, form_data) valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('groups', form.errors) self.assertEqual( form.errors['groups'][0], LDAPUserCreateForm.error_messages['group_required_superuser'] ) def test_LDAP_nonsuperuser_cannot_create_new_LDAP_superuser(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') # Should create a new user named "superuser1" who is a superuser. form_data = {'username': '******', 'is_superuser': True, 'groups': [self.wagtail_admins_group.pk]} # Confirm that this user doesn't already exist, so we know this is the "brand new user" workflow. self.assertFalse(get_user_model().objects.filter(username=form_data['username']).exists()) with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) form = LDAPUserCreateForm(request, form_data) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) # There should be no "is_superuser" entry in cleaned_data, because the is_superuser form field is excluded. self.assertNotIn('is_superuser', form.cleaned_data) # The resulting User should not be a superuser. with LogCapture() as capture: new_user = form.save() self.assertFalse(new_user.is_superuser) # ldap lookup self.assertTrue('user.update_from_ldap.success' in str(capture)) # user creation self.assertTrue('user.ldap.create' in str(capture)) self.assertTrue('Superusers' not in str(capture)) def test_LDAP_duplicate_username_error(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Create and validate the form. We expect the form to throw a dulicate_username error since you can't # create a user that's already a member of the current site. Which "wagtail_editor" is. form = LDAPUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('username', form.errors) # Create and validate the form. We expect the form to throw a dulicate_username error since we don't let # the create user workflow mess with existing superusers. form_data['username'] = '******' form = LDAPUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('username', form.errors) def test_create_LDAP_user_belonging_to_other_site_groups_is_disallowed(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') form_data = {'username': '******', 'groups': [self.test_editors_group.pk]} with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Create and validate the form. We expect the form to throw an Invalid Choice error, since Groups that # aren't connected to the current Site aren't in the choices list. form = LDAPUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('groups', form.errors) self.assertIn('Select a valid choice', form.errors['groups'][0]) def test_create_LDAP_user_with_username_that_is_not_in_LDAP(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy_no_results) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # We expect the form to throw an error about the user not existing in LDAP, because we've mocked the # search_ldap_for_user to return no results. form = LDAPUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('username', form.errors) self.assertEqual(form.errors['username'][0], LDAPUserCreateForm.error_messages['not_in_ldap']) def test_create_LDAP_user_with_LDAP_error(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy_error) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # We expect the form to throw an error about the LDAP lookup failing, because we've mocked the # search_ldap_for_user to throw an exception. form = LDAPUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('username', form.errors) self.assertEqual(form.errors['username'][0], LDAPUserCreateForm.error_messages['ldap_lookup_failed']) def test_create_LDAP_user_which_already_exists(self): request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') test_site = Site.objects.get(hostname='test.flint.oursites.com') # Should add wagtail editors group to test_admin, since test_admin already exists on the "test" site. form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} existing_user = get_user_model().objects.get(username=form_data['username']) # test_admin starts in both of the test site's Groups. self.assertEqual(len(existing_user.groups.all()), 2) self.assertTrue(user_is_member_of_site(existing_user, test_site)) self.assertFalse(user_is_member_of_site(existing_user, request.site)) with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) # Create and validate the form, then save. We expect everything to work, and for "test_editor" to now be in # two Groups. form = LDAPUserCreateForm(request, form_data) valid = form.is_valid() self.assertTrue(valid) # The resulting User should be the same user as we started with, but with another Group membership. with LogCapture() as capture: new_user = form.save() self.assertEqual(len(new_user.groups.all()), 3) self.assertTrue(user_is_member_of_site(new_user, test_site)) self.assertTrue(user_is_member_of_site(new_user, request.site)) # ldap lookup log message self.assertTrue('user.update_from_ldap.success' in str(capture)) # user creation log message self.assertTrue('user.ldap.update' in str(capture)) def test_create_LDAP_user_view_GET(self): self.login('superuser') with Replacer() as r: r.replace('wagtail_patches.views.users.LDAPUserCreateForm', self.form_dummy) # Unlike most replacements, we can replace the module's origin here because it gets imported at runtime, # rather than being imported at load time. r.replace('wagtail.wagtailadmin.messages', self.messages_dummy) # First, test to make sure the routing works. response = self.client.get(reverse('wagtailusers_users:add'), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertEqual(response.template_name, 'wagtail_patches/users/create.html') self.form_dummy.reset_dummy() # Confirm that unpriviledged users can't access the view. self.login('wagtail_editor') response = self.client.get(reverse('wagtailusers_users:add'), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailadmin_home')) self.assertEqual(len(self.messages_dummy.error.calls), 1) # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.get('/') request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') response = create(request) self.assertEqual(response.template_name, 'wagtail_patches/users/create.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) def test_create_LDAP_user_view_POST_invalid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False. r.replace('wagtail_patches.views.users.LDAPUserCreateForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # First, test to make sure the routing works. form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} response = self.client.post( reverse('wagtailusers_users:add'), data=form_data, HTTP_HOST=self.wagtail_site.hostname ) self.assertEqual(response.status_code, 200) self.assertEqual(response.template_name, 'wagtail_patches/users/create.html') self.form_dummy.reset_dummy() self.messages_dummy.reset_dummy() # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.post(reverse('wagtailusers_users:add'), form_data) request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') response = create(request) self.assertEqual(response.template_name, 'wagtail_patches/users/create.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1], request.POST) self.assertEqual(len(self.messages_dummy.success.calls), 0) self.assertEqual(len(self.messages_dummy.error.calls), 1) self.assertEqual(self.messages_dummy.error.calls[0]['args'][0], request) self.assertEqual( self.messages_dummy.error.calls[0]['args'][1], 'The user could not be created due to errors.' ) def test_create_LDAP_user_view_POST_valid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False, so we need to change that for the valid data test. self.form_dummy.default_return.is_valid = Dummy(default_return=True) r.replace('wagtail_patches.views.users.LDAPUserCreateForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) form_data = {'username': '******', 'groups': [self.wagtail_editors_group.pk]} request = self.wagtail_factory.post(reverse('wagtailusers_users:add'), form_data) request.user = get_user_model().objects.get(username='******') request.site = Site.objects.get(hostname='wagtail.flint.oursites.com') response = create(request) self.assertEqual(response.url, reverse('wagtailusers_users:index')) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1], request.POST) # form.is_valid() returned true, so the user should have been saved, had their password garbled, and been # informed about success. self.assertEqual(len(self.form_dummy.default_return.save.calls), 1) self.assertEqual(len(self.form_dummy.default_return.save.default_return.set_unusable_password.calls), 1) self.assertEqual(len(self.messages_dummy.success.calls), 1) self.assertEqual(self.messages_dummy.success.calls[0]['args'][0], request)
class TestLDAPUserEdit(SecureClientMixin, TestCase, AssertHTMLMixin, MultitenantSiteTestingMixin): @classmethod def setUpTestData(cls): cls.set_up_test_sites_and_users() cls.wagtail_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'wagtail.flint.oursites.com', }) cls.test_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'test.flint.oursites.com', }) def setUp(self): super(TestLDAPUserEdit, self).setUp() # Dummy for search_ldap_for_user(), returning good data. self.mock_email = '*****@*****.**' self.mock_sn = 'Shmoe' self.mock_given_name = 'Joe' self.ldap_search_dummy = Dummy(default_return=('fake_dn', { 'givenName': [self.mock_given_name], 'sn': [self.mock_sn], 'CAPPrimaryEmail': [self.mock_email], })) # Dummy for when search_ldap_for_user() should fail. self.ldap_search_dummy_error = lambda x: thrower( ldap.LDAPError('error!')) # NOQA # Dummy for when search_ldap_for_user() should return no results. self.ldap_search_dummy_no_results = Dummy(default_return=None) # Form dummy for view tests. self.form_dummy().is_valid() returns False by default. self.form_dummy = Dummy(default_return=Dummy( is_valid=Dummy(default_return=False), save=Dummy(default_return=Dummy())), ) # Messages dummy. self.messages_dummy = Dummy(error=Dummy(), success=Dummy(), button=Dummy()) # Dummies for forcing a password to be considered as usable or not. self.password_not_usable_dummy = Dummy(default_return=False) self.password_is_usable_dummy = Dummy(default_return=True) ############ # TESTS ############ def test_LDAP_form_init_non_superuser(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site # Create the form and make the sure the initialization worked as exepected. form = LDAPUserEditForm(request, self.wagtail_editor) self.assertEqual(form.request, request) # Non-superusers cannot change a User's is_superuser status. self.assertNotIn('is_superuser', form.fields) # Non-superusers only see the Groups' shortnames. self.assertEqual(form.fields['groups'].choices, [(self.wagtail_admins_group.pk, 'Admins'), (self.wagtail_editors_group.pk, 'Editors')]) self.assertTrue(form.fields['groups'].required) self.assertEqual(form.fields['groups'].error_messages['required'], form.error_messages['group_required']) self.assertNotIn('is_active', form.fields) def test_LDAP_form_init_superuser(self): # Only superusers can see the is_active checkbox for LDAP users request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site # Create the form and make the sure the initialization worked as exepected. form = LDAPUserEditForm(request, self.wagtail_editor) self.assertIn('is_superuser', form.fields) self.assertIn('is_active', form.fields) def test_superusers_can_be_ungrouped(self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) form_data = {'is_superuser': True, 'groups': [], 'is_active': True} form = LDAPUserEditForm(request, self.superuser, form_data) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. with LogCapture(): valid = form.is_valid() self.assertTrue(valid) # Comparing the empty form.cleaned_data['groups'] directly to the empty form_data['groups'] doesn't work, # I think because the cleaned data isn't an actualy list. Casting it to list() works, though. self.assertEqual(list(form.cleaned_data['groups']), form_data['groups']) def test_superuser_can_assign_LDAP_user_to_group_andor_set_user_as_superuser_but_not_neither( self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site with LogCapture(): with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) form_data = { 'is_superuser': True, 'groups': [], 'is_active': True } form = LDAPUserEditForm(request, self.wagtail_admin, form_data) valid = form.is_valid() self.assertTrue(valid) form_data = { 'is_superuser': True, 'groups': [self.wagtail_editors_group.pk], 'is_active': True } form = LDAPUserEditForm(request, self.wagtail_admin, form_data) valid = form.is_valid() self.assertTrue(valid) form_data = { 'is_superuser': False, 'groups': [self.wagtail_editors_group.pk], 'is_active': True } form = LDAPUserEditForm(request, self.wagtail_admin, form_data) valid = form.is_valid() self.assertTrue(valid) form_data = { 'is_superuser': False, 'groups': [], 'is_active': True } form = LDAPUserEditForm(request, self.wagtail_admin, form_data) valid = form.is_valid() self.assertFalse(valid) self.assertEqual(form.errors['groups'][0], form.error_messages['group_required_superuser']) def test_assign_LDAP_user_to_another_sites_groups_is_disallowed(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) form_data = {'groups': [self.test_editors_group.pk]} form = LDAPUserEditForm(request, self.wagtail_editor, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertIn('Select a valid choice', form.errors['groups'][0]) def test_edit_LDAP_user_with_LDAP_error_during_save(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy_error) form_data = {'groups': [self.wagtail_admins_group.pk]} form = LDAPUserEditForm(request, self.wagtail_editor, form_data) valid = form.is_valid() self.assertTrue(valid) # We use commit=False to make sure that self.wagtail_editor hasn't changed. with LogCapture() as capture: user = form.save(commit=False) # Confirm that the user's personal info hasn't changed. self.assertEqual(user.email, self.wagtail_editor.email) self.assertEqual(user.first_name, self.wagtail_editor.first_name) self.assertEqual(user.last_name, self.wagtail_editor.last_name) # ldap lookup fails because the mock is configured to fail self.assertTrue('user.update_from_ldap.failed' in str(capture)) def test_edit_LDAP_user_successfully(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site with Replacer() as r: r.replace('wagtail_patches.forms.search_ldap_for_user', self.ldap_search_dummy) r.replace('core.utils.search_ldap_for_user', self.ldap_search_dummy) self.assertSequenceEqual(self.wagtail_editor.groups.all(), [self.wagtail_editors_group]) form_data = { 'groups': [self.wagtail_admins_group.pk, self.wagtail_editors_group.pk] } form = LDAPUserEditForm(request, self.wagtail_editor, form_data) valid = form.is_valid() self.assertTrue(valid) with LogCapture() as capture: user = form.save() # Confirm that the user's personal info hasn't changed. self.assertEqual(user.email, self.mock_email) self.assertEqual(user.first_name, self.mock_given_name) self.assertEqual(user.last_name, self.mock_sn) self.assertSequenceEqual( user.groups.all(), [self.wagtail_admins_group, self.wagtail_editors_group]) # log messages self.assertTrue('user.update_from_ldap.success' in str(capture)) # groups should have been updated for our user self.assertTrue( '<Group: wagtail.flint.oursites.com Admins>' in str(capture)) def test_edit_LDAP_user_view_GET(self): self.login('wagtail_admin') with Replacer() as r: r.replace('wagtail_patches.views.users.LDAPUserEditForm', self.form_dummy) r.replace('wagtail.wagtailadmin.messages', self.messages_dummy) # Need to force the passwords to be considered unusable so that form code thinks the User is an LDAP user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_not_usable_dummy) # First, test to make sure the routing works. response = self.client.get(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'wagtail_patches/users/edit.html') self.form_dummy.reset_dummy() # Confirm that unprivileged users can't access the view. self.login('wagtail_editor') response = self.client.get(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailadmin_home')) self.assertEqual(len(self.messages_dummy.error.calls), 1) # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.template_name, 'wagtail_patches/users/edit.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) # Confirm that a non-superusers can't edit a superuser. response = edit(request, self.superuser.pk) self.assertEqual(response.url, reverse('wagtailadmin_home')) # Confirm that a non-superusers can't edit a user belonging to another Site. response = edit(request, self.test_admin.pk) self.assertEqual(response.url, reverse('wagtailadmin_home')) def test_edit_LDAP_user_view_POST_invalid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False. r.replace('wagtail_patches.views.users.LDAPUserEditForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # Need to force the passwords to be considered unusable so that form code thinks the User is an LDAP user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_not_usable_dummy) # First, test to make sure the routing works. form_data = {'groups': [self.test_editors_group.pk]} response = self.client.post(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data, HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'wagtail_patches/users/edit.html') self.form_dummy.reset_dummy() self.messages_dummy.reset_dummy() # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.post(reverse( 'wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.template_name, 'wagtail_patches/users/edit.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) self.assertEqual(self.form_dummy.calls[0]['args'][2], request.POST) self.assertEqual(len(self.messages_dummy.success.calls), 0) self.assertEqual(len(self.messages_dummy.error.calls), 1) self.assertEqual(self.messages_dummy.error.calls[0]['args'][0], request) self.assertEqual(self.messages_dummy.error.calls[0]['args'][1], 'The user could not be saved due to errors.') def test_edit_LDAP_user_view_POST_valid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False, so we need to change that for the valid data test. self.form_dummy.default_return.is_valid = Dummy( default_return=True) r.replace('wagtail_patches.views.users.LDAPUserEditForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # Need to force the passwords to be considered unusable so that form code thinks the User is an LDAP user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_not_usable_dummy) form_data = { 'groups': [self.wagtail_admins_group.pk, self.wagtail_editors_group.pk] } request = self.wagtail_factory.post(reverse( 'wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailusers_users:index')) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) self.assertEqual(self.form_dummy.calls[0]['args'][2], request.POST) # form.is_valid() returned true, so the user should have been saved and been informed about success. self.assertEqual(len(self.form_dummy.default_return.save.calls), 1) self.assertEqual(len(self.messages_dummy.success.calls), 1) self.assertEqual(self.messages_dummy.success.calls[0]['args'][0], request) def test_admin_user_sees_remove_user_button(self): ldap_user = UserFactory(username='******', first_name='LDAP', last_name='User', groups=[self.wagtail_editors_group]) ldap_user.set_unusable_password() ldap_user.save() self.login('wagtail_admin') response = self.client.get(reverse('wagtailusers_users:edit', args=[ldap_user.pk]), HTTP_HOST="wagtail.flint.oursites.com") self.assertEqual(response.status_code, 200) with self.assertHTML(response.content, 'div.name') as (name, ): self.assertTrue(name.text_content().endswith('LDAP User')) with self.assertHTML(response.content, 'ul.button-bar li.right a') as (button, ): self.assertEqual(button.text.strip(), "Remove User From This Site") self.assertNotHTML(response.content, 'input[name="is_active"]') def test_superuser_sees_active_checkbox(self): ldap_user = UserFactory(username='******', first_name='LDAP', last_name='User', groups=[self.wagtail_editors_group]) ldap_user.set_unusable_password() ldap_user.save() self.login('superuser') response = self.client.get(reverse('wagtailusers_users:edit', args=[ldap_user.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) with self.assertHTML(response.content, 'div.name') as (name, ): self.assertTrue(name.text_content().endswith('LDAP User')) with self.assertHTML(response.content, 'input[name="is_active"]') as checkbox: self.assertEqual(len(checkbox), 1) self.assertNotHTML(response.content, 'ul.button-bar li.right a') def test_admin_remove_user_button_removes_user(self): ldap_user = UserFactory(username='******', first_name='LDAP', last_name='User', groups=[self.wagtail_editors_group]) ldap_user.set_unusable_password() ldap_user.save() # confirm the user is in the wagtail_editors group self.assertTrue( self.wagtail_editors_group.user_set.filter( pk=ldap_user.pk).exists()) self.login('wagtail_admin') response = self.client.get(reverse('wagtailusers_users:edit', args=[ldap_user.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) with self.assertHTML(response.content, 'ul.button-bar li.right a') as (button, ): response = self.client.get(button.attrib['href'], HTTP_HOST=self.wagtail_site.hostname) self.assertTrue(isinstance(response, HttpResponseRedirect)) self.assertEqual(response.url, reverse('wagtailusers_users:index')) # confirm the user is no longer in the wagtail_editors group self.assertFalse( self.wagtail_editors_group.user_set.filter( pk=ldap_user.pk).exists())
class TestLocalUserEdit(SecureClientMixin, TestCase, MultitenantSiteTestingMixin): @classmethod def setUpTestData(cls): cls.set_up_test_sites_and_users() cls.wagtail_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'wagtail.flint.oursites.com', }) cls.test_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'test.flint.oursites.com', }) def setUp(self): super(TestLocalUserEdit, self).setUp() # Logger dummy. self.logger_dummy = Dummy(warning=Dummy(), info=Dummy()) # Form dummy for view tests. self.form_dummy().is_valid() returns False by default. self.form_dummy = Dummy(default_return=Dummy( is_valid=Dummy(default_return=False), save=Dummy(default_return=Dummy())), ) # Messages dummy. self.messages_dummy = Dummy(error=Dummy(), success=Dummy(), button=Dummy()) # Dummies for forcing a password to be considered as usable or not. self.password_not_usable_dummy = Dummy(default_return=False) self.password_is_usable_dummy = Dummy(default_return=True) def safe_get_user(self, user): """ This function exists due to a quirk in our test setup. Because we store a persistant instance of each of the Users created during test init (e.g. self.wagtail_editor), those instances don't get reset when the TransactionalTestCase rolls back the changes from the previous test. So, to avoid problems with inconsistent test data, we mustn't ever pass e.g. self.wagtail_editor directly into the edit form. """ return get_user_model().objects.get(pk=user.pk) ############ # TESTS ############ def test_local_form_init(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site # Create the form and make the sure the initialization worked as exepected. form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_editor)) self.assertEqual(form.request, request) # Non-superusers cannot change a User's is_superuser status. self.assertNotIn('is_superuser', form.fields) # Non-superusers only see the Groups' shortnames. self.assertEqual(form.fields['groups'].choices, [(self.wagtail_admins_group.pk, 'Admins'), (self.wagtail_editors_group.pk, 'Editors')]) self.assertTrue(form.fields['groups'].required) self.assertEqual(form.fields['groups'].error_messages['required'], form.error_messages['group_required']) self.assertIn('uncheck this box', form.fields['is_active'].help_text) def test_superusers_can_be_ungrouped(self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'groups': [], 'is_superuser': True, 'is_active': True, } form = LocalUserEditForm(request, self.superuser, form_data) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. with LogCapture(): valid = form.is_valid() self.assertTrue(valid) # Comparing the empty form.cleaned_data['groups'] directly to the empty form_data['groups'] doesn't work, # I think because the cleaned data isn't an actualy list. Casting it to list() works, though. self.assertEqual(list(form.cleaned_data['groups']), form_data['groups']) def test_superuser_can_assign_local_user_to_group_andor_set_user_as_superuser_but_not_neither( self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'groups': [], 'is_superuser': True, 'is_active': True, } with LogCapture(): form_data.update({ 'is_superuser': True, 'groups': [], 'is_active': True }) form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_admin), form_data) valid = form.is_valid() self.assertTrue(valid) form_data.update({ 'is_superuser': True, 'groups': [self.wagtail_editors_group.pk], 'is_active': True }) form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_admin), form_data) valid = form.is_valid() self.assertTrue(valid) form_data.update({ 'is_superuser': False, 'groups': [self.wagtail_editors_group.pk], 'is_active': True }) form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_admin), form_data) valid = form.is_valid() self.assertTrue(valid) form_data.update({ 'is_superuser': False, 'groups': [], 'is_active': True }) form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_admin), form_data) valid = form.is_valid() self.assertFalse(valid) self.assertEqual(form.errors['groups'][0], form.error_messages['group_required_superuser']) def test_assign_local_user_to_another_sites_groups_is_disallowed(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'groups': [self.test_editors_group.pk], 'is_active': True, } with LogCapture(): form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_editor), form_data) valid = form.is_valid() self.assertFalse(valid) self.assertIn('Select a valid choice', form.errors['groups'][0]) def test_edit_local_user_successfully(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'groups': [self.wagtail_admins_group.pk, self.wagtail_editors_group.pk], 'is_active': True, } with LogCapture() as capture: self.assertSequenceEqual(self.wagtail_editor.groups.all(), [self.wagtail_editors_group]) form = LocalUserEditForm(request, self.safe_get_user(self.wagtail_editor), form_data) valid = form.is_valid() self.assertTrue(valid) user = form.save() # Confirm that the user's personal info now matches the form_data. self.assertEqual( user.username, request.site.hostname + '-' + form_data['username']) self.assertEqual(user.email, form_data['email']) self.assertEqual(user.first_name, form_data['first_name']) self.assertEqual(user.last_name, form_data['last_name']) self.assertSequenceEqual( user.groups.all(), [self.wagtail_admins_group, self.wagtail_editors_group]) self.assertIn("user.local.update", str(capture)) self.assertIn("'username'", str(capture)) self.assertIn("'email'", str(capture)) self.assertIn("'first_name'", str(capture)) self.assertIn("'last_name'", str(capture)) def test_edit_local_user_view_GET(self): self.login('wagtail_admin') with Replacer() as r: r.replace('wagtail_patches.views.users.LocalUserEditForm', self.form_dummy) r.replace('wagtail.wagtailadmin.messages', self.messages_dummy) # Need to force the passwords to be considered usable so that form code thinks the User is a Local user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_is_usable_dummy) # First, test to make sure the routing works. response = self.client.get(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'wagtail_patches/users/edit.html') self.form_dummy.reset_dummy() # Confirm that unpriviledged users can't access the view. self.login('wagtail_editor') response = self.client.get(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailadmin_home')) self.assertEqual(len(self.messages_dummy.error.calls), 1) # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.template_name, 'wagtail_patches/users/edit.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) # Confirm that a non-superusers can't edit a superuser. response = edit(request, self.superuser.pk) self.assertEqual(response.url, reverse('wagtailadmin_home')) # Confirm that a non-superusers can't edit a user belonging to another Site. response = edit(request, self.test_admin.pk) self.assertEqual(response.url, reverse('wagtailadmin_home')) def test_edit_local_user_view_POST_invalid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False. r.replace('wagtail_patches.views.users.LocalUserEditForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # Need to force the passwords to be considered usable so that form code thinks the User is a Local user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_is_usable_dummy) # First, test to make sure the routing works. form_data = {'groups': [self.test_editors_group.pk]} response = self.client.post(reverse('wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data, HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, 'wagtail_patches/users/edit.html') self.form_dummy.reset_dummy() self.messages_dummy.reset_dummy() # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.post(reverse( 'wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.template_name, 'wagtail_patches/users/edit.html') self.assertEqual(response.context_data['form'], self.form_dummy.default_return) self.assertEqual(response.context_data['user'].pk, self.wagtail_editor.pk) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) self.assertEqual(self.form_dummy.calls[0]['args'][2], request.POST) self.assertEqual(len(self.messages_dummy.success.calls), 0) self.assertEqual(len(self.messages_dummy.error.calls), 1) self.assertEqual(self.messages_dummy.error.calls[0]['args'][0], request) self.assertEqual(self.messages_dummy.error.calls[0]['args'][1], 'The user could not be saved due to errors.') def test_edit_local_user_view_POST_valid_data(self): self.login('superuser') with Replacer() as r: # By default, the form dummy's is_valid() returns False, so we need to change that for the valid data test. self.form_dummy.default_return.is_valid = Dummy( default_return=True) r.replace('wagtail_patches.views.users.LocalUserEditForm', self.form_dummy) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # Need to force the passwords to be considered usable so that form code thinks the User is a Local user. r.replace('django.contrib.auth.base_user.is_password_usable', self.password_is_usable_dummy) form_data = { 'groups': [self.wagtail_admins_group.pk, self.wagtail_editors_group.pk] } request = self.wagtail_factory.post(reverse( 'wagtailusers_users:edit', args=[self.wagtail_editor.pk]), data=form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = edit(request, self.wagtail_editor.pk) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailusers_users:index')) self.assertEqual(len(self.form_dummy.calls), 1) self.assertEqual(self.form_dummy.calls[0]['args'][0], request) self.assertEqual(self.form_dummy.calls[0]['args'][1].pk, self.wagtail_editor.pk) self.assertEqual(self.form_dummy.calls[0]['args'][2], request.POST) # form.is_valid() returned true, so the user should have been saved and been informed about success. self.assertEqual(len(self.form_dummy.default_return.save.calls), 1) self.assertEqual(len(self.messages_dummy.success.calls), 1) self.assertEqual(self.messages_dummy.success.calls[0]['args'][0], request)
class TestLocalUserCreate(SecureClientMixin, TestCase, MultitenantSiteTestingMixin): @classmethod def setUpTestData(cls): cls.set_up_test_sites_and_users() cls.wagtail_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'wagtail.flint.oursites.com', }) cls.test_factory = RequestFactory( **{ 'wsgi.url_scheme': 'https', 'SERVER_NAME': 'test.flint.oursites.com', }) def setUp(self): super(TestLocalUserCreate, self).setUp() # Logger dummy self.logger_dummy = Dummy(warning=Dummy(), info=Dummy(), bind=Dummy()) # Form dummy, for view tests self.form_dummy_invalid = Dummy(default_return=Dummy( is_valid=Dummy(default_return=False), save=Dummy(default_return=Dummy())), ) self.form_dummy_valid = Dummy(default_return=Dummy( is_valid=Dummy(default_return=True), save=Dummy(default_return=Dummy())), ) # Messages dummy self.messages_dummy = Dummy(error=Dummy(), success=Dummy(), button=Dummy()) ############ # TESTS ############ def test_create_brand_new_local_nonsuperuser_happy_path(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site # Should create a new user named "wagtail_user1" in the "wagtail.flint.oursites.com Admins" Group. form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, 'groups': [self.wagtail_admins_group.pk], } # Confirm that this user doesn't already exist, so we know this is the "brand new user" workflow. self.assertFalse(get_user_model().objects.filter( username=form_data['username']).exists()) # Create the form and make the sure the initialization worked as exepected. form = LocalUserCreateForm(request, form_data) # Non-superusers cannot set a new user as a superuser. self.assertNotIn('is_superuser', form.fields) # Non-superusers only see the Groups' shortnames. self.assertEqual(form.fields['groups'].choices, [(self.wagtail_admins_group.pk, 'Admins'), (self.wagtail_editors_group.pk, 'Editors')]) self.assertTrue(form.fields['groups'].required) self.assertEqual(form.fields['groups'].error_messages['required'], form.error_messages['group_required']) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) with LogCapture() as capture: new_user = form.save() # the username in the database should automatically be prefixed with the # current site hostname self.assertEqual(new_user.username, request.site.hostname + "-" + form_data['username']) self.assertEqual(new_user.email, form_data['email']) self.assertEqual(new_user.first_name, form_data['first_name']) self.assertEqual(new_user.last_name, form_data['last_name']) self.assertTrue(new_user.check_password(form_data['password1'])) self.assertTrue(user_is_member_of_site(new_user, request.site)) self.assertEqual(list(new_user.groups.all()), [self.wagtail_admins_group]) self.assertIn('user.local.create', str(capture)) def test_create_brand_new_local_superuser_happy_path(self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site # Should create a new user named "superuser1" who is a superuser. form_data = { 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, 'is_superuser': True, 'username': '******', } # Confirm that this user doesn't already exist. self.assertFalse(get_user_model().objects.filter( username=form_data['username']).exists()) # Create the form and make the sure the initialization worked as exepected. form = LocalUserCreateForm(request, form_data) # Superusers CAN set a new user as a superuser. self.assertIn('is_superuser', form.fields) # Superusers see all Groups' full names. self.assertSequenceEqual( # Need to convert to a list here, since it's currently a ModelChoiceIterator. list(form.fields['groups'].choices), [(g.pk, g.name) for g in Group.objects.all()]) self.assertFalse(form.fields['groups'].required) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) with LogCapture() as capture: new_user = form.save() # local superusers' usernames in the database should NOT be prefixed # with the local site name self.assertEqual(new_user.username, form_data['username']) self.assertTrue(new_user.is_superuser) self.assertEqual(new_user.email, form_data['email']) self.assertEqual(new_user.first_name, form_data['first_name']) self.assertEqual(new_user.last_name, form_data['last_name']) self.assertTrue(new_user.check_password(form_data['password1'])) self.assertFalse(user_is_member_of_site(new_user, request.site)) # Make sure we logged it correctly. self.assertIn('user.local.create', str(capture)) self.assertIsNotNone(re.search("'groups': '.*Superusers", str(capture))) def test_superuser_has_to_assign_local_user_to_group_or_set_superuser( self): request = self.wagtail_factory.get('/') request.user = self.superuser request.site = self.wagtail_site # Should fail to create a user since no group or superuser flag is set. form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, } # Confirm that this user doesn't already exist. self.assertFalse(get_user_model().objects.filter( username=form_data['username']).exists()) # Form_data doesn't set a Group or the is_superuser flag, so the form should throw an error. form = LocalUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('groups', form.errors) self.assertEqual( form.errors['groups'][0], LocalUserCreateForm.error_messages['group_required_superuser']) def test_local_nonsuperuser_cannot_create_new_superuser(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site # Would create a new user named "superuser1" who is a superuser, if that were allowed. form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, 'is_superuser': True, 'groups': [self.wagtail_admins_group.pk], } # Confirm that this user doesn't already exist. self.assertFalse(get_user_model().objects.filter( username=form_data['username']).exists()) form = LocalUserCreateForm(request, form_data) # Validate the form. We save the results to a variable due to a quirk in PyDev's debugger regarding forms. valid = form.is_valid() self.assertTrue(valid) # There should be no "is_superuser" entry in cleaned_data, because the is_superuser form field is excluded. self.assertNotIn('is_superuser', form.cleaned_data) # The resulting User should not be a superuser. with LogCapture() as capture: new_user = form.save() self.assertFalse(new_user.is_superuser) # Make sure we logged it correctly. self.assertIn('user.local.create', str(capture)) self.assertIsNone(re.search("'groups': '.*Superusers", str(capture))) def test_local_duplicate_username_error(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = { # A User named wagtail_editor already exists. 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, 'groups': [self.wagtail_editors_group.pk], } # We expect the form to throw a dulicate_username error since you can't # create a local User with a username that's already in use. We created # our local wagtail_editor_local already in setUpTestData(). form = LocalUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('username', form.errors) def test_create_local_user_belonging_to_other_site_groups_is_disallowed( self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': DEFAULT_PASSWORD, 'groups': [self.test_editors_group.pk], } # Create and validate the form. We expect the form to throw an Invalid # Choice error, since Groups that aren't connected to the current Site # aren't in the choices list. form = LocalUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('groups', form.errors) self.assertIn('Select a valid choice', form.errors['groups'][0]) def test_create_local_user_password_confirmation_field(self): request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site form_data = { 'username': '******', 'email': '*****@*****.**', 'first_name': 'John', 'last_name': 'Wagtail', 'password1': DEFAULT_PASSWORD, 'password2': 'blah', 'groups': [self.wagtail_editors_group.pk], } # Create and validate the form. We expect the form to throw an Invalid # Choice error, since Groups that aren't connected to the current Site # aren't in the choices list. form = LocalUserCreateForm(request, form_data) with LogCapture(): valid = form.is_valid() self.assertFalse(valid) self.assertEqual(len(form.errors), 1) self.assertIn('password2', form.errors) self.assertEqual(form.error_messages['password_mismatch'], form.errors['password2'][0]) def test_create_local_user_view_GET(self): self.login('superuser') with Replacer() as r: r.replace('wagtail_patches.views.users.LocalUserCreateForm', self.form_dummy_valid) # Unlike most replacements, we can replace the module's origin here because it gets imported at runtime, # rather than being imported at load time. r.replace('wagtail.wagtailadmin.messages', self.messages_dummy) # First, test to make sure the routing works. response = self.client.get(reverse('wagtailusers_users:add_local'), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertEqual(response.template_name, 'wagtail_patches/users/create_local.html') self.form_dummy_valid.reset_dummy() # Confirm that unpriviledged users can't access the view. self.login('wagtail_editor') response = self.client.get(reverse('wagtailusers_users:add_local'), HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 302) self.assertEqual(response.url, reverse('wagtailadmin_home')) self.assertEqual(len(self.messages_dummy.error.calls), 1) # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.get('/') request.user = self.wagtail_admin request.site = self.wagtail_site response = create_local(request) self.assertEqual(response.template_name, 'wagtail_patches/users/create_local.html') self.assertEqual(response.context_data['form'], self.form_dummy_valid.default_return) self.assertEqual(len(self.form_dummy_valid.calls), 1) self.assertEqual(self.form_dummy_valid.calls[0]['args'][0], request) def test_create_local_user_view_POST_invalid_data(self): self.login('superuser') with Replacer() as r: # By default, form_dummy.is_valid() returns False. r.replace('wagtail_patches.views.users.LocalUserCreateForm', self.form_dummy_invalid) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # We're forcing is_vald to return False, so it doesn't matter what this data actually is. form_data = {'username': '******'} # First, test to make sure the routing works. response = self.client.post( reverse('wagtailusers_users:add_local'), data=form_data, HTTP_HOST=self.wagtail_site.hostname) self.assertEqual(response.status_code, 200) self.assertEqual(response.template_name, 'wagtail_patches/users/create_local.html') self.form_dummy_invalid.reset_dummy() self.messages_dummy.reset_dummy() # Now, the real unit test requires us to call the view function directly, since it's not # consistently possible to retrieve the request object from the output of the http testing client. request = self.wagtail_factory.post( reverse('wagtailusers_users:add_local'), form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = create_local(request) self.assertEqual(response.template_name, 'wagtail_patches/users/create_local.html') self.assertEqual(response.context_data['form'], self.form_dummy_invalid.default_return) self.assertEqual(len(self.form_dummy_invalid.calls), 1) self.assertEqual(self.form_dummy_invalid.calls[0]['args'][0], request) self.assertEqual(self.form_dummy_invalid.calls[0]['args'][1], request.POST) self.assertEqual(len(self.messages_dummy.success.calls), 0) self.assertEqual(len(self.messages_dummy.error.calls), 1) self.assertEqual(self.messages_dummy.error.calls[0]['args'][0], request) self.assertEqual(self.messages_dummy.error.calls[0]['args'][1], 'The user could not be created due to errors.') def test_create_local_user_view_POST_valid_data(self): self.login('superuser') with Replacer() as r: r.replace('wagtail_patches.views.users.LocalUserCreateForm', self.form_dummy_valid) r.replace('wagtail_patches.views.users.messages', self.messages_dummy) # We're forcing is_vald to return True, so it doesn't matter what this data actually is. form_data = { 'username': '******', 'groups': [self.wagtail_editors_group.pk] } request = self.wagtail_factory.post( reverse('wagtailusers_users:add_local'), form_data) request.user = self.wagtail_admin request.site = self.wagtail_site response = create_local(request) self.assertEqual(response.url, reverse('wagtailusers_users:index')) self.assertEqual(len(self.form_dummy_valid.calls), 1) self.assertEqual(self.form_dummy_valid.calls[0]['args'][0], request) self.assertEqual(self.form_dummy_valid.calls[0]['args'][1], request.POST) # form.is_valid() returned true, so the user should have been saved, had their password garbled, and been # informed about success. self.assertEqual( len(self.form_dummy_valid.default_return.save.calls), 1) self.assertEqual(len(self.messages_dummy.success.calls), 1) self.assertEqual(self.messages_dummy.success.calls[0]['args'][0], request)