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)
Example #2
0
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)
Example #3
0
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())
Example #4
0
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)