Repository URL to install this package:
Version:
2.4.1b1 ▾
|
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.core.exceptions import ImproperlyConfigured
from django.core.files.uploadedfile import SimpleUploadedFile
from django.http import HttpRequest, HttpResponse
from django.test import TestCase, override_settings
from django.urls import reverse
from wagtail.core import hooks
from wagtail.core.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME
from wagtail.core.models import Collection, GroupCollectionPermission, GroupPagePermission, Page
from wagtail.tests.utils import WagtailTestUtils
from wagtail.users.forms import UserCreationForm, UserEditForm
from wagtail.users.models import UserProfile
from wagtail.users.views.users import get_user_creation_form, get_user_edit_form
delete_user_perm_codename = "delete_{0}".format(AUTH_USER_MODEL_NAME.lower())
change_user_perm_codename = "change_{0}".format(AUTH_USER_MODEL_NAME.lower())
def test_avatar_provider(user, default, size=50):
return '/nonexistent/path/to/avatar.png'
class CustomUserCreationForm(UserCreationForm):
country = forms.CharField(required=True, label="Country")
attachment = forms.FileField(required=True, label="Attachment")
class CustomUserEditForm(UserEditForm):
country = forms.CharField(required=True, label="Country")
attachment = forms.FileField(required=True, label="Attachment")
class TestUserFormHelpers(TestCase):
def test_get_user_edit_form_with_default_form(self):
user_form = get_user_edit_form()
self.assertIs(user_form, UserEditForm)
def test_get_user_creation_form_with_default_form(self):
user_form = get_user_creation_form()
self.assertIs(user_form, UserCreationForm)
@override_settings(
WAGTAIL_USER_CREATION_FORM='wagtail.users.tests.CustomUserCreationForm'
)
def test_get_user_creation_form_with_custom_form(self):
user_form = get_user_creation_form()
self.assertIs(user_form, CustomUserCreationForm)
@override_settings(
WAGTAIL_USER_EDIT_FORM='wagtail.users.tests.CustomUserEditForm'
)
def test_get_user_edit_form_with_custom_form(self):
user_form = get_user_edit_form()
self.assertIs(user_form, CustomUserEditForm)
@override_settings(
WAGTAIL_USER_CREATION_FORM='wagtail.users.tests.CustomUserCreationFormDoesNotExist'
)
def test_get_user_creation_form_with_invalid_form(self):
self.assertRaises(ImproperlyConfigured, get_user_creation_form)
@override_settings(
WAGTAIL_USER_EDIT_FORM='wagtail.users.tests.CustomUserEditFormDoesNotExist'
)
def test_get_user_edit_form_with_invalid_form(self):
self.assertRaises(ImproperlyConfigured, get_user_edit_form)
class TestUserIndexView(TestCase, WagtailTestUtils):
def setUp(self):
# create a user that should be visible in the listing
self.test_user = get_user_model().objects.create_user(
username='testuser',
email='testuser@email.com',
password='password',
first_name='First Name',
last_name='Last Name'
)
self.login()
def get(self, params={}):
return self.client.get(reverse('wagtailusers_users:index'), params)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/index.html')
self.assertContains(response, 'testuser')
def test_allows_negative_ids(self):
# see https://github.com/wagtail/wagtail/issues/565
get_user_model().objects.create_user('guardian', 'guardian@example.com', 'gu@rd14n', pk=-1)
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'testuser')
self.assertContains(response, 'guardian')
def test_search(self):
response = self.get({'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['query_string'], "Hello")
def test_search_query_one_field(self):
response = self.get({'q': "first name"})
self.assertEqual(response.status_code, 200)
results = response.context['users'].object_list
self.assertIn(self.test_user, results)
def test_search_query_multiple_fields(self):
response = self.get({'q': "first name last name"})
self.assertEqual(response.status_code, 200)
results = response.context['users'].object_list
self.assertIn(self.test_user, results)
def test_pagination(self):
pages = ['0', '1', '-1', '9999', 'Not a page']
for page in pages:
response = self.get({'p': page})
self.assertEqual(response.status_code, 200)
class TestUserCreateView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def get(self, params={}):
return self.client.get(reverse('wagtailusers_users:add'), params)
def post(self, post_data={}):
return self.client.post(reverse('wagtailusers_users:add'), post_data)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertContains(response, 'Password:')
self.assertContains(response, 'Password confirmation:')
def test_create(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "password",
'password2': "password",
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 1)
self.assertEqual(users.first().email, 'test@user.com')
@override_settings(
WAGTAIL_USER_CREATION_FORM='wagtail.users.tests.CustomUserCreationForm',
WAGTAIL_USER_CUSTOM_FIELDS=['country', 'document'],
)
def test_create_with_custom_form(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "password",
'password2': "password",
'country': "testcountry",
'attachment': SimpleUploadedFile('test.txt', b"Uploaded file"),
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 1)
self.assertEqual(users.first().email, 'test@user.com')
self.assertEqual(users.first().country, 'testcountry')
self.assertEqual(users.first().attachment.read(), b"Uploaded file")
def test_create_with_password_mismatch(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "password1",
'password2': "password2",
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertTrue(response.context['form'].errors['password2'])
# Check that the user was not created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
@override_settings(
AUTH_PASSWORD_VALIDATORS=[
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
],
)
def test_create_with_password_validation(self):
"""
Test that the Django password validators are run when creating a user.
Specifically test that the UserAttributeSimilarityValidator works,
which requires a full-populated user model before the validation works.
"""
# Create a user with a password the same as their name
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Example",
'last_name': "Name",
'password1': "example name",
'password2': "example name",
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
# Password field should have an error
errors = response.context['form'].errors.as_data()
self.assertIn('password2', errors)
self.assertEqual(errors['password2'][0].code, 'password_too_similar')
# Check that the user was not created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
def test_create_with_missing_password(self):
"""Password should be required by default"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "",
'password2': "",
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertTrue(response.context['form'].errors['password1'])
# Check that the user was not created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
@override_settings(WAGTAILUSERS_PASSWORD_REQUIRED=False)
def test_password_fields_exist_when_not_required(self):
"""Password fields should still be shown if WAGTAILUSERS_PASSWORD_REQUIRED is False"""
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertContains(response, 'Password:')
self.assertContains(response, 'Password confirmation:')
@override_settings(WAGTAILUSERS_PASSWORD_REQUIRED=False)
def test_create_with_password_not_required(self):
"""Password should not be required if WAGTAILUSERS_PASSWORD_REQUIRED is False"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "",
'password2': "",
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 1)
self.assertEqual(users.first().email, 'test@user.com')
self.assertEqual(users.first().password, '')
@override_settings(WAGTAILUSERS_PASSWORD_REQUIRED=False)
def test_optional_password_is_still_validated(self):
"""When WAGTAILUSERS_PASSWORD_REQUIRED is False, password validation should still apply if a password _is_ supplied"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "banana",
'password2': "kumquat",
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertTrue(response.context['form'].errors['password2'])
# Check that the user was not created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
@override_settings(WAGTAILUSERS_PASSWORD_REQUIRED=False)
def test_password_still_accepted_when_optional(self):
"""When WAGTAILUSERS_PASSWORD_REQUIRED is False, we should still allow a password to be set"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "banana",
'password2': "banana",
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 1)
self.assertEqual(users.first().email, 'test@user.com')
self.assertTrue(users.first().check_password('banana'))
@override_settings(WAGTAILUSERS_PASSWORD_ENABLED=False)
def test_password_fields_not_shown_when_disabled(self):
"""WAGTAILUSERS_PASSWORD_ENABLED=False should cause password fields to be removed"""
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/create.html')
self.assertNotContains(response, 'Password:')
self.assertNotContains(response, 'Password confirmation:')
@override_settings(WAGTAILUSERS_PASSWORD_ENABLED=False)
def test_password_fields_ignored_when_disabled(self):
"""When WAGTAILUSERS_PASSWORD_ENABLED is False, users should always be created without a usable password"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Test",
'last_name': "User",
'password1': "banana", # not part of the form - should be ignored
'password2': "kumquat", # not part of the form - should be ignored
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was created
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 1)
self.assertEqual(users.first().email, 'test@user.com')
self.assertEqual(users.first().password, '')
def test_before_create_user_hook(self):
def hook_func(request):
self.assertIsInstance(request, HttpRequest)
return HttpResponse("Overridden!")
with self.register_hook('before_create_user', hook_func):
response = self.client.get(
reverse('wagtailusers_users:add')
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_create_user_hook_post(self):
def hook_func(request):
self.assertIsInstance(request, HttpRequest)
return HttpResponse("Overridden!")
with self.register_hook('before_create_user', hook_func):
post_data = {
'username': "testuser",
'email': "testuser@test.com",
'password1': 'password12',
'password2': 'password12',
'first_name': 'test',
'last_name': 'user',
}
response = self.client.post(
reverse('wagtailusers_users:add'),
post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_after_create_user_hook(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(user, get_user_model())
return HttpResponse("Overridden!")
with self.register_hook('after_create_user', hook_func):
post_data = {
'username': "testuser",
'email': "testuser@test.com",
'password1': 'password12',
'password2': 'password12',
'first_name': 'test',
'last_name': 'user',
}
response = self.client.post(
reverse('wagtailusers_users:add'),
post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
class TestUserDeleteView(TestCase, WagtailTestUtils):
def setUp(self):
# create a user that should be visible in the listing
self.test_user = get_user_model().objects.create_user(
username='testuser',
email='testuser@email.com',
password='password'
)
# also create a superuser to delete
self.superuser = get_user_model().objects.create_superuser(
username='testsuperuser',
email='testsuperuser@email.com',
password='password'
)
self.current_user = self.login()
def get(self, params={}):
return self.client.get(reverse('wagtailusers_users:delete', args=(self.test_user.pk,)), params)
def post(self, post_data={}):
return self.client.post(reverse('wagtailusers_users:delete', args=(self.test_user.pk,)), post_data)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/confirm_delete.html')
def test_delete(self):
response = self.post()
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was deleted
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
def test_user_cannot_delete_self(self):
response = self.client.get(reverse('wagtailusers_users:delete', args=(self.current_user.pk,)))
# Should redirect to admin index (permission denied)
self.assertRedirects(response, reverse('wagtailadmin_home'))
# Check user was not deleted
self.assertTrue(get_user_model().objects.filter(pk=self.current_user.pk).exists())
def test_user_can_delete_other_superuser(self):
response = self.client.get(reverse('wagtailusers_users:delete', args=(self.superuser.pk,)))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/confirm_delete.html')
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.superuser.pk,)))
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was deleted
users = get_user_model().objects.filter(username='testsuperuser')
self.assertEqual(users.count(), 0)
def test_before_delete_user_hook(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.pk, self.test_user.pk)
return HttpResponse("Overridden!")
with self.register_hook('before_delete_user', hook_func):
response = self.client.get(reverse('wagtailusers_users:delete', args=(self.test_user.pk, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_delete_user_hook_post(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.pk, self.test_user.pk)
return HttpResponse("Overridden!")
with self.register_hook('before_delete_user', hook_func):
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.test_user.pk, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_after_delete_user_hook(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.username, self.test_user.username)
return HttpResponse("Overridden!")
with self.register_hook('after_delete_user', hook_func):
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.test_user.pk, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
class TestUserDeleteViewForNonSuperuser(TestCase, WagtailTestUtils):
def setUp(self):
# create a user that should be visible in the listing
self.test_user = get_user_model().objects.create_user(
username='testuser',
email='testuser@email.com',
password='password'
)
# create a user with delete permission
self.deleter_user = get_user_model().objects.create_user(
username='deleter',
email='deleter@email.com',
password='password'
)
deleters_group = Group.objects.create(name='User deleters')
deleters_group.permissions.add(Permission.objects.get(codename='access_admin'))
deleters_group.permissions.add(Permission.objects.get(
content_type__app_label=AUTH_USER_APP_LABEL, codename=delete_user_perm_codename
))
self.deleter_user.groups.add(deleters_group)
self.superuser = self.create_test_user()
self.client.login(username='deleter', password='password')
def test_simple(self):
response = self.client.get(reverse('wagtailusers_users:delete', args=(self.test_user.pk,)))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/confirm_delete.html')
def test_delete(self):
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.test_user.pk,)))
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was deleted
users = get_user_model().objects.filter(username='testuser')
self.assertEqual(users.count(), 0)
def test_user_cannot_delete_self(self):
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.deleter_user.pk,)))
# Should redirect to admin index (permission denied)
self.assertRedirects(response, reverse('wagtailadmin_home'))
# Check user was not deleted
self.assertTrue(get_user_model().objects.filter(pk=self.deleter_user.pk).exists())
def test_user_cannot_delete_superuser(self):
response = self.client.post(reverse('wagtailusers_users:delete', args=(self.superuser.pk,)))
# Should redirect to admin index (permission denied)
self.assertRedirects(response, reverse('wagtailadmin_home'))
# Check user was not deleted
self.assertTrue(get_user_model().objects.filter(pk=self.superuser.pk).exists())
class TestUserEditView(TestCase, WagtailTestUtils):
def setUp(self):
# Create a user to edit
self.test_user = get_user_model().objects.create_user(
username='testuser',
email='testuser@email.com',
first_name='Original',
last_name='User',
password='password'
)
# Login
self.current_user = self.login()
def get(self, params={}, user_id=None):
return self.client.get(reverse('wagtailusers_users:edit', args=(user_id or self.test_user.pk, )), params)
def post(self, post_data={}, user_id=None):
return self.client.post(reverse('wagtailusers_users:edit', args=(user_id or self.test_user.pk, )), post_data)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/edit.html')
self.assertContains(response, 'Password:')
self.assertContains(response, 'Password confirmation:')
def test_nonexistant_redirect(self):
self.assertEqual(self.get(user_id=100000).status_code, 404)
def test_simple_post(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "newpassword",
'password2': "newpassword",
'is_active': 'on'
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Edited')
self.assertTrue(user.check_password('newpassword'))
def test_password_optional(self):
"""Leaving password fields blank should leave it unchanged"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "",
'password2': "",
'is_active': 'on'
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited but password is unchanged
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Edited')
self.assertTrue(user.check_password('password'))
def test_passwords_match(self):
"""Password fields should be validated if supplied"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "banana",
'password2': "kumquat",
'is_active': 'on'
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/edit.html')
self.assertTrue(response.context['form'].errors['password2'])
# Check that the user was not edited
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Original')
self.assertTrue(user.check_password('password'))
@override_settings(
AUTH_PASSWORD_VALIDATORS=[
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
],
)
def test_edit_with_password_validation(self):
"""
Test that the Django password validators are run when editing a user.
Specifically test that the UserAttributeSimilarityValidator works,
which requires a full-populated user model before the validation works.
"""
# Create a user with a password the same as their name
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "Name",
'password1': "edited name",
'password2': "edited name",
})
# Should remain on page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/edit.html')
# Password field should have an error
errors = response.context['form'].errors.as_data()
self.assertIn('password2', errors)
self.assertEqual(errors['password2'][0].code, 'password_too_similar')
# Check that the user was not edited
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Original')
self.assertTrue(user.check_password('password'))
def test_edit_and_deactivate(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "password",
'password2': "password",
# Leaving out these fields, thus setting them to False:
# 'is_active': 'on'
# 'is_superuser': 'on',
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Edited')
# Check that the user is no longer superuser
self.assertEqual(user.is_superuser, False)
# Check that the user is no longer active
self.assertEqual(user.is_active, False)
def test_edit_and_make_superuser(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "password",
'password2': "password",
'is_active': 'on',
'is_superuser': 'on',
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.test_user.pk)
# Check that the user is now superuser
self.assertEqual(user.is_superuser, True)
# Check that the user is now active
self.assertEqual(user.is_active, True)
def test_edit_self(self):
response = self.post({
'username': 'test@email.com',
'email': 'test@email.com',
'first_name': "Edited Myself",
'last_name': "User",
# 'password1': "password",
# 'password2': "password",
'is_active': 'on',
'is_superuser': 'on',
}, self.current_user.pk)
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.current_user.pk)
self.assertEqual(user.first_name, 'Edited Myself')
# Check that the user is still superuser
self.assertEqual(user.is_superuser, True)
# Check that the user is still active
self.assertEqual(user.is_active, True)
def test_editing_own_password_does_not_log_out(self):
response = self.post({
'username': 'test@email.com',
'email': 'test@email.com',
'first_name': "Edited Myself",
'last_name': "User",
'password1': "c0rrecth0rse",
'password2': "c0rrecth0rse",
'is_active': 'on',
'is_superuser': 'on',
}, self.current_user.pk)
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.current_user.pk)
self.assertEqual(user.first_name, 'Edited Myself')
# Check user is not logged out
response = self.client.get(reverse('wagtailusers_users:index'))
self.assertEqual(response.status_code, 200)
def test_cannot_demote_self(self):
"""
check that unsetting a user's own is_active or is_superuser flag has no effect
"""
response = self.post({
'username': 'test@email.com',
'email': 'test@email.com',
'first_name': "Edited Myself",
'last_name': "User",
# 'password1': "password",
# 'password2': "password",
# failing to submit is_active or is_superuser would unset those flags,
# if we didn't explicitly prevent that when editing self
# 'is_active': 'on',
# 'is_superuser': 'on',
}, self.current_user.pk)
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.current_user.pk)
self.assertEqual(user.first_name, 'Edited Myself')
# Check that the user is still superuser
self.assertEqual(user.is_superuser, True)
# Check that the user is still active
self.assertEqual(user.is_active, True)
@override_settings(
WAGTAIL_USER_EDIT_FORM='wagtail.users.tests.CustomUserEditForm',
)
def test_edit_with_custom_form(self):
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "password",
'password2': "password",
'country': "testcountry",
'attachment': SimpleUploadedFile('test.txt', b"Uploaded file"),
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Edited')
self.assertEqual(user.country, 'testcountry')
self.assertEqual(user.attachment.read(), b"Uploaded file")
def test_edit_validation_error(self):
# Leave "username" field blank. This should give a validation error
response = self.post({
'username': "",
'email': "test@user.com",
'first_name': "Teset",
'last_name': "User",
'password1': "password",
'password2': "password",
})
# Should not redirect to index
self.assertEqual(response.status_code, 200)
@override_settings(WAGTAILUSERS_PASSWORD_ENABLED=False)
def test_password_fields_not_shown_when_disabled(self):
"""WAGTAILUSERS_PASSWORD_ENABLED=False should cause password fields to be removed"""
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/users/edit.html')
self.assertNotContains(response, 'Password:')
self.assertNotContains(response, 'Password confirmation:')
@override_settings(WAGTAILUSERS_PASSWORD_ENABLED=False)
def test_password_fields_ignored_when_disabled(self):
"""When WAGTAILUSERS_PASSWORD_REQUIRED is False, existing password should be left unchanged"""
response = self.post({
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'is_active': 'on',
'password1': "banana", # not part of the form - should be ignored
'password2': "kumquat", # not part of the form - should be ignored
})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
# Check that the user was edited but password is unchanged
user = get_user_model().objects.get(pk=self.test_user.pk)
self.assertEqual(user.first_name, 'Edited')
self.assertTrue(user.check_password('password'))
def test_before_edit_user_hook(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.pk, self.test_user.pk)
return HttpResponse("Overridden!")
with self.register_hook('before_edit_user', hook_func):
response = self.client.get(reverse('wagtailusers_users:edit', args=(self.test_user.pk, )))
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_edit_user_hook_post(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.pk, self.test_user.pk)
return HttpResponse("Overridden!")
with self.register_hook('before_edit_user', hook_func):
post_data = {
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "password",
'password2': "password",
}
response = self.client.post(
reverse('wagtailusers_users:edit', args=(self.test_user.pk, )), post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_after_edit_user_hook_post(self):
def hook_func(request, user):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(user.pk, self.test_user.pk)
return HttpResponse("Overridden!")
with self.register_hook('after_edit_user', hook_func):
post_data = {
'username': "testuser",
'email': "test@user.com",
'first_name': "Edited",
'last_name': "User",
'password1': "password",
'password2': "password",
}
response = self.client.post(
reverse('wagtailusers_users:edit', args=(self.test_user.pk, )), post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
class TestUserProfileCreation(TestCase, WagtailTestUtils):
def setUp(self):
# Create a user
self.test_user = get_user_model().objects.create_user(
username='testuser',
email='testuser@email.com',
password='password',
)
def test_user_created_without_profile(self):
self.assertEqual(UserProfile.objects.filter(user=self.test_user).count(), 0)
with self.assertRaises(UserProfile.DoesNotExist):
self.test_user.wagtail_userprofile
def test_user_profile_created_when_method_called(self):
self.assertIsInstance(UserProfile.get_for_user(self.test_user), UserProfile)
# and get it from the db too
self.assertEqual(UserProfile.objects.filter(user=self.test_user).count(), 1)
def test_avatar_empty_on_profile_creation(self):
user_profile = UserProfile.get_for_user(self.test_user)
self.assertFalse(user_profile.avatar)
class TestUserEditViewForNonSuperuser(TestCase, WagtailTestUtils):
def setUp(self):
# create a user with edit permission
self.editor_user = get_user_model().objects.create_user(
username='editor',
email='editor@email.com',
password='password'
)
editors_group = Group.objects.create(name='User editors')
editors_group.permissions.add(Permission.objects.get(codename='access_admin'))
editors_group.permissions.add(Permission.objects.get(
content_type__app_label=AUTH_USER_APP_LABEL, codename=change_user_perm_codename
))
self.editor_user.groups.add(editors_group)
self.client.login(username='editor', password='password')
def test_user_cannot_escalate_privileges(self):
"""
Check that a non-superuser cannot edit their own is_active or is_superuser flag.
(note: this doesn't necessarily guard against other routes to escalating privileges, such
as creating a new user with is_superuser=True or adding oneself to a group with additional
privileges - the latter will be dealt with by #537)
"""
editors_group = Group.objects.get(name='User editors')
post_data = {
'username': "editor",
'email': "editor@email.com",
'first_name': "Escalating",
'last_name': "User",
'password1': "",
'password2': "",
'groups': [editors_group.id, ],
# These should not be possible without manipulating the form in the DOM:
'is_superuser': 'on',
'is_active': 'on',
}
response = self.client.post(
reverse('wagtailusers_users:edit', args=(self.editor_user.pk, )),
post_data)
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_users:index'))
user = get_user_model().objects.get(pk=self.editor_user.pk)
# check if user is still in the editors group
self.assertTrue(user.groups.filter(name='User editors').exists())
# check that non-permission-related edits went ahead
self.assertEqual(user.first_name, "Escalating")
# Check that the user did not escalate its is_superuser status
self.assertEqual(user.is_superuser, False)
class TestGroupIndexView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
def get(self, params={}):
return self.client.get(reverse('wagtailusers_groups:index'), params)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/groups/index.html')
def test_search(self):
response = self.get({'q': "Hello"})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['search_form']['q'].value(), "Hello")
class TestGroupCreateView(TestCase, WagtailTestUtils):
def setUp(self):
self.login()
self.add_doc_permission = Permission.objects.get(
content_type__app_label='wagtaildocs', codename='add_document'
)
self.change_doc_permission = Permission.objects.get(
content_type__app_label='wagtaildocs', codename='change_document'
)
def get(self, params={}):
return self.client.get(reverse('wagtailusers_groups:add'), params)
def post(self, post_data={}):
post_defaults = {
'page_permissions-TOTAL_FORMS': ['0'],
'page_permissions-MAX_NUM_FORMS': ['1000'],
'page_permissions-INITIAL_FORMS': ['0'],
'document_permissions-TOTAL_FORMS': ['0'],
'document_permissions-MAX_NUM_FORMS': ['1000'],
'document_permissions-INITIAL_FORMS': ['0'],
'image_permissions-TOTAL_FORMS': ['0'],
'image_permissions-MAX_NUM_FORMS': ['1000'],
'image_permissions-INITIAL_FORMS': ['0'],
}
for k, v in post_defaults.items():
post_data[k] = post_data.get(k, v)
return self.client.post(reverse('wagtailusers_groups:add'), post_data)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/groups/create.html')
def test_create_group(self):
response = self.post({'name': "test group"})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# Check that the user was created
groups = Group.objects.filter(name='test group')
self.assertEqual(groups.count(), 1)
def test_group_create_adding_permissions(self):
response = self.post({
'name': "test group",
'page_permissions-0-page': ['1'],
'page_permissions-0-permission_types': ['edit', 'publish'],
'page_permissions-TOTAL_FORMS': ['1'],
'document_permissions-0-collection': [Collection.get_first_root_node().pk],
'document_permissions-0-permissions': [self.add_doc_permission.pk],
'document_permissions-TOTAL_FORMS': ['1'],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now exists, with two page permissions
# and one 'add document' collection permission
new_group = Group.objects.get(name='test group')
self.assertEqual(new_group.page_permissions.all().count(), 2)
self.assertEqual(
new_group.collection_permissions.filter(permission=self.add_doc_permission).count(),
1
)
def test_duplicate_page_permissions_error(self):
# Try to submit multiple page permission entries for the same page
response = self.post({
'name': "test group",
'page_permissions-0-page': ['1'],
'page_permissions-0-permission_types': ['publish'],
'page_permissions-1-page': ['1'],
'page_permissions-1-permission_types': ['edit'],
'page_permissions-TOTAL_FORMS': ['2'],
})
self.assertEqual(response.status_code, 200)
# formset should have a non-form error about the duplication
self.assertTrue(response.context['permission_panels'][0].non_form_errors)
def test_duplicate_document_permissions_error(self):
# Try to submit multiple document permission entries for the same collection
root_collection = Collection.get_first_root_node()
response = self.post({
'name': "test group",
'document_permissions-0-collection': [root_collection.pk],
'document_permissions-0-permissions': [self.add_doc_permission.pk],
'document_permissions-1-collection': [root_collection.pk],
'document_permissions-1-permissions': [self.change_doc_permission.pk],
'document_permissions-TOTAL_FORMS': ['2'],
})
self.assertEqual(response.status_code, 200)
# formset should have a non-form error about the duplication
# (we don't know what index in permission_panels the formset will be,
# so just assert that it happens on at least one permission_panel)
self.assertTrue(
any(
hasattr(panel, 'non_form_errors') and panel.non_form_errors
for panel in response.context['permission_panels']
)
)
def test_can_submit_blank_permission_form(self):
# the formsets for page / collection permissions should gracefully
# handle (and ignore) forms that have been left entirely blank
response = self.post({
'name': "test group",
'page_permissions-0-page': [''],
'page_permissions-TOTAL_FORMS': ['1'],
'document_permissions-0-collection': [''],
'document_permissions-TOTAL_FORMS': ['1'],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now exists, with no page / document permissions
new_group = Group.objects.get(name='test group')
self.assertEqual(new_group.page_permissions.all().count(), 0)
self.assertEqual(
new_group.collection_permissions.filter(permission=self.add_doc_permission).count(),
0
)
class TestGroupEditView(TestCase, WagtailTestUtils):
def setUp(self):
# Create a group to edit
self.test_group = Group.objects.create(name='test group')
self.root_page = Page.objects.get(pk=1)
self.root_add_permission = GroupPagePermission.objects.create(page=self.root_page,
permission_type='add',
group=self.test_group)
self.home_page = Page.objects.get(pk=2)
# Get the hook-registered permissions, and add one to this group
self.registered_permissions = Permission.objects.none()
for fn in hooks.get_hooks('register_permissions'):
self.registered_permissions = self.registered_permissions | fn()
self.existing_permission = self.registered_permissions.order_by('pk')[0]
self.another_permission = self.registered_permissions.order_by('pk')[1]
self.test_group.permissions.add(self.existing_permission)
# set up collections to test document permissions
self.root_collection = Collection.get_first_root_node()
self.evil_plans_collection = self.root_collection.add_child(name="Evil plans")
self.add_doc_permission = Permission.objects.get(
content_type__app_label='wagtaildocs', codename='add_document'
)
self.change_doc_permission = Permission.objects.get(
content_type__app_label='wagtaildocs', codename='change_document'
)
GroupCollectionPermission.objects.create(
group=self.test_group,
collection=self.evil_plans_collection,
permission=self.add_doc_permission,
)
# Login
self.login()
def get(self, params={}, group_id=None):
return self.client.get(reverse('wagtailusers_groups:edit', args=(group_id or self.test_group.pk, )), params)
def post(self, post_data={}, group_id=None):
post_defaults = {
'name': 'test group',
'permissions': [self.existing_permission.pk],
'page_permissions-TOTAL_FORMS': ['1'],
'page_permissions-MAX_NUM_FORMS': ['1000'],
'page_permissions-INITIAL_FORMS': ['1'],
'page_permissions-0-page': [self.root_page.pk],
'page_permissions-0-permission_types': ['add'],
'document_permissions-TOTAL_FORMS': ['1'],
'document_permissions-MAX_NUM_FORMS': ['1000'],
'document_permissions-INITIAL_FORMS': ['1'],
'document_permissions-0-collection': [self.evil_plans_collection.pk],
'document_permissions-0-permissions': [self.add_doc_permission.pk],
'image_permissions-TOTAL_FORMS': ['0'],
'image_permissions-MAX_NUM_FORMS': ['1000'],
'image_permissions-INITIAL_FORMS': ['0'],
}
for k, v in post_defaults.items():
post_data[k] = post_data.get(k, v)
return self.client.post(reverse(
'wagtailusers_groups:edit', args=(group_id or self.test_group.pk, )), post_data)
def add_non_registered_perm(self):
# Some groups may have django permissions assigned that are not
# hook-registered as part of the wagtail interface. We need to ensure
# that these permissions are not overwritten by our views.
# Tests that use this method are testing the aforementioned
# functionality.
self.non_registered_perms = Permission.objects.exclude(pk__in=self.registered_permissions)
self.non_registered_perm = self.non_registered_perms[0]
self.test_group.permissions.add(self.non_registered_perm)
def test_simple(self):
response = self.get()
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'wagtailusers/groups/edit.html')
def test_nonexistant_group_redirect(self):
self.assertEqual(self.get(group_id=100000).status_code, 404)
def test_group_edit(self):
response = self.post({'name': "test group edited"})
# Should redirect back to index
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# Check that the group was edited
group = Group.objects.get(pk=self.test_group.pk)
self.assertEqual(group.name, 'test group edited')
def test_group_edit_validation_error(self):
# Leave "name" field blank. This should give a validation error
response = self.post({'name': ""})
# Should not redirect to index
self.assertEqual(response.status_code, 200)
def test_group_edit_adding_page_permissions_same_page(self):
# The test group has one page permission to begin with - 'add' permission on root.
# Add two additional permission types on the root page
self.assertEqual(self.test_group.page_permissions.count(), 1)
response = self.post({
'page_permissions-0-permission_types': ['add', 'publish', 'edit'],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now has three page permissions
self.assertEqual(self.test_group.page_permissions.count(), 3)
def test_group_edit_adding_document_permissions_same_collection(self):
# The test group has one document permission to begin with -
# 'add' permission on evil_plans.
# Add 'change' permission on evil_plans
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
1
)
response = self.post({
'document_permissions-0-permissions': [
self.add_doc_permission.pk, self.change_doc_permission.pk
],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now has two document permissions
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
2
)
def test_group_edit_adding_document_permissions_different_collection(self):
# The test group has one document permission to begin with -
# 'add' permission on evil_plans.
# Add 'add' and 'change' permission on the root collection
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
1
)
response = self.post({
'document_permissions-TOTAL_FORMS': ['2'],
'document_permissions-1-collection': [self.root_collection.pk],
'document_permissions-1-permissions': [
self.add_doc_permission.pk, self.change_doc_permission.pk
],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now has three document permissions
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
3
)
def test_group_edit_deleting_page_permissions(self):
# The test group has one page permission to begin with
self.assertEqual(self.test_group.page_permissions.count(), 1)
response = self.post({
'page_permissions-0-DELETE': ['1'],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now has zero page permissions
self.assertEqual(self.test_group.page_permissions.count(), 0)
def test_group_edit_deleting_document_permissions(self):
# The test group has one document permission to begin with
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
1
)
response = self.post({
'document_permissions-0-DELETE': ['1'],
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
# The test group now has zero document permissions
self.assertEqual(
self.test_group.collection_permissions.filter(
permission__content_type__app_label='wagtaildocs'
).count(),
0
)
def test_group_edit_loads_with_django_permissions_shown(self):
# the checkbox for self.existing_permission should be ticked
response = self.get()
# use allow_extra_attrs because the input will also have an id (with an unpredictable value)
self.assertTagInHTML(
'<input name="permissions" type="checkbox" checked value="%s">' % self.existing_permission.id,
str(response.content),
allow_extra_attrs=True)
def test_group_edit_loads_with_page_permissions_shown(self):
# The test group has one page permission to begin with
self.assertEqual(self.test_group.page_permissions.count(), 1)
response = self.get()
page_permissions_formset = response.context['permission_panels'][0]
self.assertEqual(
page_permissions_formset.management_form['INITIAL_FORMS'].value(),
1
)
self.assertEqual(
page_permissions_formset.forms[0]['page'].value(),
self.root_page.pk
)
self.assertEqual(
page_permissions_formset.forms[0]['permission_types'].value(),
['add']
)
# add edit permission on root
GroupPagePermission.objects.create(
page=self.root_page, permission_type='edit', group=self.test_group
)
# The test group now has two page permissions on root (but only one form covering both)
self.assertEqual(self.test_group.page_permissions.count(), 2)
# Reload the page and check the form instances
response = self.get()
page_permissions_formset = response.context['permission_panels'][0]
self.assertEqual(page_permissions_formset.management_form['INITIAL_FORMS'].value(), 1)
self.assertEqual(len(page_permissions_formset.forms), 1)
self.assertEqual(
page_permissions_formset.forms[0]['page'].value(),
self.root_page.pk
)
self.assertEqual(
set(page_permissions_formset.forms[0]['permission_types'].value()),
set(['add', 'edit'])
)
# add edit permission on home
GroupPagePermission.objects.create(
page=self.home_page, permission_type='edit', group=self.test_group
)
# The test group now has three page permissions, over two forms
self.assertEqual(self.test_group.page_permissions.count(), 3)
# Reload the page and check the form instances
response = self.get()
page_permissions_formset = response.context['permission_panels'][0]
self.assertEqual(page_permissions_formset.management_form['INITIAL_FORMS'].value(), 2)
self.assertEqual(
page_permissions_formset.forms[0]['page'].value(),
self.root_page.pk
)
self.assertEqual(
set(page_permissions_formset.forms[0]['permission_types'].value()),
set(['add', 'edit'])
)
self.assertEqual(
page_permissions_formset.forms[1]['page'].value(),
self.home_page.pk
)
self.assertEqual(
page_permissions_formset.forms[1]['permission_types'].value(),
['edit']
)
def test_duplicate_page_permissions_error(self):
# Try to submit multiple page permission entries for the same page
response = self.post({
'page_permissions-1-page': [self.root_page.pk],
'page_permissions-1-permission_types': ['edit'],
'page_permissions-TOTAL_FORMS': ['2'],
})
self.assertEqual(response.status_code, 200)
# the formset should have a non-form error
self.assertTrue(response.context['permission_panels'][0].non_form_errors)
def test_duplicate_document_permissions_error(self):
# Try to submit multiple document permission entries for the same collection
response = self.post({
'document_permissions-1-page': [self.evil_plans_collection.pk],
'document_permissions-1-permissions': [self.change_doc_permission],
'document_permissions-TOTAL_FORMS': ['2'],
})
self.assertEqual(response.status_code, 200)
# the formset should have a non-form error
self.assertTrue(
any(
hasattr(panel, 'non_form_errors') and panel.non_form_errors
for panel in response.context['permission_panels']
)
)
def test_group_add_registered_django_permissions(self):
# The test group has one django permission to begin with
self.assertEqual(self.test_group.permissions.count(), 1)
response = self.post({
'permissions': [self.existing_permission.pk, self.another_permission.pk]
})
self.assertRedirects(response, reverse('wagtailusers_groups:index'))
self.assertEqual(self.test_group.permissions.count(), 2)
def test_group_retains_non_registered_permissions_when_editing(self):
self.add_non_registered_perm()
original_permissions = list(self.test_group.permissions.all()) # list() to force evaluation
# submit the form with no changes (only submitting the exsisting
# permission, as in the self.post function definition)
self.post()
# See that the group has the same permissions as before
self.assertEqual(list(self.test_group.permissions.all()), original_permissions)
self.assertEqual(self.test_group.permissions.count(), 2)
def test_group_retains_non_registered_permissions_when_adding(self):
self.add_non_registered_perm()
# Add a second registered permission
self.post({
'permissions': [self.existing_permission.pk, self.another_permission.pk]
})
# See that there are now three permissions in total
self.assertEqual(self.test_group.permissions.count(), 3)
# ...including the non-registered one
self.assertIn(self.non_registered_perm, self.test_group.permissions.all())
def test_group_retains_non_registered_permissions_when_deleting(self):
self.add_non_registered_perm()
# Delete all registered permissions
self.post({'permissions': []})
# See that the non-registered permission is still there
self.assertEqual(self.test_group.permissions.count(), 1)
self.assertEqual(self.test_group.permissions.all()[0], self.non_registered_perm)