kopia lustrzana https://github.com/wagtail/wagtail
Update PageViewRestriction model to support group/login restriction type
Update page privacy form with login/group options Add unit tests for setting group-based permissions Add a basic login page to wagtailcore Implement front-end logic for group and login-based permissions Allow overriding the frontend login template name with WAGTAIL_FRONTEND_LOGIN_TEMPLATE Add documentation for customising the login page Add message on login page for authenticated users who don't have accesspull/3123/head
rodzic
2442416e93
commit
59440c92f1
|
@ -3,9 +3,33 @@
|
|||
Private pages
|
||||
=============
|
||||
|
||||
Users with publish permission on a page can set it to be private by clicking the 'Privacy' control in the top right corner of the page explorer or editing interface, and setting a password. Users visiting this page, or any of its subpages, will be prompted to enter a password before they can view the page.
|
||||
Users with publish permission on a page can set it to be private by clicking the 'Privacy' control in the top right corner of the page explorer or editing interface. This sets a restriction on who is allowed to view the page and its sub-pages. Several different kinds of restriction are available:
|
||||
|
||||
Private pages work on Wagtail out of the box - the site implementer does not need to do anything to set them up. However, the default "password required" form is only a bare-bones HTML page, and site implementers may wish to replace this with a page customised to their site design.
|
||||
* **Accessible to logged-in users:** The user must log in to view the page. All user accounts are granted access, regardless of permission level.
|
||||
* **Accessible with the following password:** The user must enter the given password to view the page. This is appropriate for situations where you want to share a page with a trusted group of people, but giving them individual user accounts would be overkill. The same password is shared between all users, and this works independently of any user accounts that exist on the site.
|
||||
* **Accessible to users in specific groups:** The user must be logged in, and a member of one or more of the specified groups, in order to view the page.
|
||||
|
||||
Private pages work on Wagtail out of the box - the site implementer does not need to do anything to set them up. However, the default "log in" and "password required" forms are only bare-bones HTML pages, and site implementers may wish to replace them with a page customised to their site design.
|
||||
|
||||
|
||||
Setting up a login page
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The basic login page can be customised by setting ``WAGTAIL_FRONTEND_LOGIN_TEMPLATE`` to the path of a template you wish to use:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAIL_FRONTEND_LOGIN_TEMPLATE = 'myapp/login.html'
|
||||
|
||||
Wagtail uses Django's standard ``django.contrib.auth.views.login`` view here, and so the context variables available on the template are as detailed in `Django's login view documentation <https://docs.djangoproject.com/en/1.10/topics/auth/default/#django.contrib.auth.views.login>`_.
|
||||
|
||||
If the stock Django login view is not suitable - for example, you wish to use an external authentication system, or you are integrating Wagtail into an existing Django site that already has a working login view - you can specify the URL of the login view via the ``WAGTAIL_FRONTEND_LOGIN_URL`` setting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
WAGTAIL_FRONTEND_LOGIN_URL = '/accounts/login/'
|
||||
|
||||
To integrate Wagtail into a Django site with an existing login mechanism, setting ``WAGTAIL_FRONTEND_LOGIN_URL = LOGIN_URL`` will usually be sufficient.
|
||||
|
||||
|
||||
Setting up a global "password required" page
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"model": "wagtailcore.page",
|
||||
"fields": {
|
||||
"title": "Welcome to the Wagtail test site!",
|
||||
"numchild": 7,
|
||||
"numchild": 9,
|
||||
"show_in_menus": false,
|
||||
"live": true,
|
||||
"depth": 2,
|
||||
|
@ -526,6 +526,51 @@
|
|||
"submit_time": "2014-01-01T12:00:00.000Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 18,
|
||||
"model": "wagtailcore.page",
|
||||
"fields": {
|
||||
"title": "Secret event editor plans",
|
||||
"numchild": 0,
|
||||
"show_in_menus": true,
|
||||
"live": true,
|
||||
"depth": 3,
|
||||
"content_type": ["tests", "simplepage"],
|
||||
"path": "000100010008",
|
||||
"url_path": "/home/secret-event-editor-plans/",
|
||||
"slug": "secret-event-editor-plans"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 18,
|
||||
"model": "tests.simplepage",
|
||||
"fields": {
|
||||
"content": "<p>let's move Easter to Christmas</p>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 19,
|
||||
"model": "wagtailcore.page",
|
||||
"fields": {
|
||||
"title": "Secret login plans",
|
||||
"numchild": 0,
|
||||
"show_in_menus": true,
|
||||
"live": true,
|
||||
"depth": 3,
|
||||
"content_type": ["tests", "simplepage"],
|
||||
"path": "000100010009",
|
||||
"url_path": "/home/secret-login-plans/",
|
||||
"slug": "secret-login-plans"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 19,
|
||||
"model": "tests.simplepage",
|
||||
"fields": {
|
||||
"content": "<p>collect logs</p>"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"pk": 1,
|
||||
|
@ -833,9 +878,29 @@
|
|||
"model": "wagtailcore.pageviewrestriction",
|
||||
"fields": {
|
||||
"page": 11,
|
||||
"restriction_type": "password",
|
||||
"password": "swordfish"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 2,
|
||||
"model": "wagtailcore.pageviewrestriction",
|
||||
"fields": {
|
||||
"page": 18,
|
||||
"restriction_type": "groups",
|
||||
"groups": [
|
||||
["Event editors"]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 3,
|
||||
"model": "wagtailcore.pageviewrestriction",
|
||||
"fields": {
|
||||
"page": 19,
|
||||
"restriction_type": "login"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pk": 1,
|
||||
"model": "wagtailimages.image",
|
||||
|
|
|
@ -20,7 +20,9 @@ from modelcluster.forms import ClusterForm, ClusterFormMetaclass
|
|||
from taggit.managers import TaggableManager
|
||||
|
||||
from wagtail.wagtailadmin import widgets
|
||||
from wagtail.wagtailcore.models import Collection, GroupCollectionPermission, Page
|
||||
from wagtail.wagtailcore.models import (
|
||||
Collection, GroupCollectionPermission, Page, PageViewRestriction
|
||||
)
|
||||
|
||||
|
||||
class URLOrAbsolutePathValidator(validators.URLValidator):
|
||||
|
@ -179,21 +181,32 @@ class CopyForm(forms.Form):
|
|||
return cleaned_data
|
||||
|
||||
|
||||
class PageViewRestrictionForm(forms.Form):
|
||||
restriction_type = forms.ChoiceField(label=ugettext_lazy("Visibility"), choices=[
|
||||
('none', ugettext_lazy("Public")),
|
||||
('password', ugettext_lazy("Private, accessible with the following password")),
|
||||
], widget=forms.RadioSelect)
|
||||
password = forms.CharField(label=ugettext_lazy("Password"), required=False)
|
||||
class PageViewRestrictionForm(forms.ModelForm):
|
||||
restriction_type = forms.ChoiceField(
|
||||
label=ugettext_lazy("Visibility"), choices=PageViewRestriction.RESTRICTION_CHOICES,
|
||||
widget=forms.RadioSelect)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super(PageViewRestrictionForm, self).clean()
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PageViewRestrictionForm, self).__init__(*args, **kwargs)
|
||||
|
||||
if cleaned_data.get('restriction_type') == 'password' and not cleaned_data.get('password'):
|
||||
self._errors["password"] = self.error_class([_('This field is required.')])
|
||||
del cleaned_data['password']
|
||||
self.fields['groups'].widget = forms.CheckboxSelectMultiple()
|
||||
self.fields['groups'].queryset = Group.objects.all()
|
||||
|
||||
return cleaned_data
|
||||
def clean_password(self):
|
||||
password = self.cleaned_data.get('password')
|
||||
if self.cleaned_data.get('restriction_type') == PageViewRestriction.PASSWORD and not password:
|
||||
raise forms.ValidationError(_("This field is required."), code='invalid')
|
||||
return password
|
||||
|
||||
def clean_groups(self):
|
||||
groups = self.cleaned_data.get('groups')
|
||||
if self.cleaned_data.get('restriction_type') == PageViewRestriction.GROUPS and not groups:
|
||||
raise forms.ValidationError(_("Please select at least one group."), code='invalid')
|
||||
return groups
|
||||
|
||||
class Meta:
|
||||
model = PageViewRestriction
|
||||
fields = ('restriction_type', 'password', 'groups')
|
||||
|
||||
|
||||
# Form field properties to override whenever we encounter a model field
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
{% include "wagtailadmin/shared/field_as_li.html" with field=form.restriction_type %}
|
||||
{% include "wagtailadmin/shared/field_as_li.html" with field=form.password li_classes="password-field" %}
|
||||
</ul>
|
||||
<ul class="fields" id="groups-fields">
|
||||
{% include "wagtailadmin/shared/field_as_li.html" with field=form.groups %}
|
||||
</ul>
|
||||
<input type="submit" value="{% trans "Save" %}" class="button" />
|
||||
</form>
|
||||
|
||||
|
|
|
@ -5,12 +5,20 @@ function(modal) {
|
|||
});
|
||||
|
||||
var restrictionTypePasswordField = $("input[name='restriction_type'][value='password']", modal.body);
|
||||
var passwordField = $("#id_password", modal.body);
|
||||
var restrictionTypeGroupsField = $("input[name='restriction_type'][value='groups']", modal.body);
|
||||
var passwordField = $(".password-field", modal.body);
|
||||
var groupsFields = $('#groups-fields', modal.body);
|
||||
|
||||
function refreshFormFields() {
|
||||
if (restrictionTypePasswordField.is(':checked')) {
|
||||
passwordField.removeAttr('disabled');
|
||||
passwordField.show();
|
||||
groupsFields.hide();
|
||||
} else if (restrictionTypeGroupsField.is(':checked')){
|
||||
passwordField.hide();
|
||||
groupsFields.show();
|
||||
} else {
|
||||
passwordField.attr('disabled', true);
|
||||
passwordField.hide();
|
||||
groupsFields.hide();
|
||||
}
|
||||
}
|
||||
refreshFormFields();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
|
||||
|
@ -26,7 +27,9 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
content="hello",
|
||||
live=True,
|
||||
))
|
||||
PageViewRestriction.objects.create(page=self.private_page, password='password123')
|
||||
PageViewRestriction.objects.create(
|
||||
page=self.private_page, restriction_type='password', password='password123'
|
||||
)
|
||||
|
||||
self.private_child_page = self.private_page.add_child(instance=SimplePage(
|
||||
title="Private child page",
|
||||
|
@ -34,6 +37,23 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
live=True,
|
||||
))
|
||||
|
||||
self.private_groups_page = self.homepage.add_child(instance=SimplePage(
|
||||
title="Private groups page",
|
||||
content="hello",
|
||||
live=True,
|
||||
))
|
||||
restriction = PageViewRestriction.objects.create(page=self.private_groups_page, restriction_type='groups')
|
||||
self.group = Group.objects.create(name='Private page group')
|
||||
self.group2 = Group.objects.create(name='Private page group2')
|
||||
restriction.groups.add(self.group)
|
||||
restriction.groups.add(self.group2)
|
||||
|
||||
self.private_groups_child_page = self.private_groups_page.add_child(instance=SimplePage(
|
||||
title="Private groups child page",
|
||||
content="hello",
|
||||
live=True,
|
||||
))
|
||||
|
||||
def test_get_public(self):
|
||||
"""
|
||||
This tests that a blank form is returned when a user opens the set_privacy view on a public page
|
||||
|
@ -63,6 +83,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
# Check form attributes
|
||||
self.assertEqual(response.context['form']['restriction_type'].value(), 'password')
|
||||
self.assertEqual(response.context['form']['password'].value(), 'password123')
|
||||
self.assertEqual(response.context['form']['groups'].value(), [])
|
||||
|
||||
def test_get_private_child(self):
|
||||
"""
|
||||
|
@ -83,6 +104,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
post_data = {
|
||||
'restriction_type': 'password',
|
||||
'password': 'helloworld',
|
||||
'groups': [],
|
||||
}
|
||||
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
|
||||
|
||||
|
@ -92,9 +114,16 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
|
||||
# Check that a page restriction has been created
|
||||
self.assertTrue(PageViewRestriction.objects.filter(page=self.public_page).exists())
|
||||
restriction = PageViewRestriction.objects.get(page=self.public_page)
|
||||
|
||||
# Check that the password is set correctly
|
||||
self.assertEqual(PageViewRestriction.objects.get(page=self.public_page).password, 'helloworld')
|
||||
self.assertEqual(restriction.password, 'helloworld')
|
||||
|
||||
# Check that the restriction_type is set correctly
|
||||
self.assertEqual(restriction.restriction_type, 'password')
|
||||
|
||||
# Be sure there are no groups set
|
||||
self.assertEqual(restriction.groups.count(), 0)
|
||||
|
||||
def test_set_password_restriction_password_unset(self):
|
||||
"""
|
||||
|
@ -103,6 +132,7 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
post_data = {
|
||||
'restriction_type': 'password',
|
||||
'password': '',
|
||||
'groups': [],
|
||||
}
|
||||
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
|
||||
|
||||
|
@ -119,6 +149,91 @@ class TestSetPrivacyView(TestCase, WagtailTestUtils):
|
|||
post_data = {
|
||||
'restriction_type': 'none',
|
||||
'password': '',
|
||||
'groups': [],
|
||||
}
|
||||
response = self.client.post(
|
||||
reverse('wagtailadmin_pages:set_privacy', args=(self.private_page.id, )), post_data)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "modal.respond('setPermission', true);")
|
||||
|
||||
# Check that the page restriction has been deleted
|
||||
self.assertFalse(PageViewRestriction.objects.filter(page=self.private_page).exists())
|
||||
|
||||
def test_get_private_groups(self):
|
||||
"""
|
||||
This tests that the restriction type and group fields as set correctly when a user opens the set_privacy view on a public page
|
||||
"""
|
||||
response = self.client.get(reverse('wagtailadmin_pages:set_privacy', args=(self.private_groups_page.id, )))
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, 'wagtailadmin/page_privacy/set_privacy.html')
|
||||
self.assertEqual(response.context['page'].specific, self.private_groups_page)
|
||||
|
||||
# Check form attributes
|
||||
self.assertEqual(response.context['form']['restriction_type'].value(), 'groups')
|
||||
self.assertEqual(response.context['form']['password'].value(), '')
|
||||
self.assertEqual(response.context['form']['groups'].value(), [self.group.id, self.group2.id])
|
||||
|
||||
def test_set_group_restriction(self):
|
||||
"""
|
||||
This tests that setting a group restriction using the set_privacy view works
|
||||
"""
|
||||
post_data = {
|
||||
'restriction_type': 'groups',
|
||||
'password': '',
|
||||
'groups': [self.group.id, self.group2.id],
|
||||
}
|
||||
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "modal.respond('setPermission', false);")
|
||||
|
||||
# Check that a page restriction has been created
|
||||
self.assertTrue(PageViewRestriction.objects.filter(page=self.public_page).exists())
|
||||
|
||||
restriction = PageViewRestriction.objects.get(page=self.public_page)
|
||||
|
||||
# restriction_type should be 'groups'
|
||||
self.assertEqual(restriction.restriction_type, 'groups')
|
||||
|
||||
# Be sure there is no password set
|
||||
self.assertEqual(restriction.password, '')
|
||||
|
||||
# Check that the groups are set correctly
|
||||
self.assertEqual(
|
||||
set(PageViewRestriction.objects.get(page=self.public_page).groups.all()),
|
||||
set([self.group, self.group2])
|
||||
)
|
||||
|
||||
def test_set_group_restriction_password_unset(self):
|
||||
"""
|
||||
This tests that the group fields on the form are validated correctly
|
||||
"""
|
||||
post_data = {
|
||||
'restriction_type': 'groups',
|
||||
'password': '',
|
||||
'groups': [],
|
||||
}
|
||||
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.public_page.id, )), post_data)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Check that a form error was raised
|
||||
self.assertFormError(response, 'form', 'groups', "Please select at least one group.")
|
||||
|
||||
def test_unset_group_restriction(self):
|
||||
"""
|
||||
This tests that removing a groups restriction using the set_privacy view works
|
||||
"""
|
||||
post_data = {
|
||||
'restriction_type': 'none',
|
||||
'password': '',
|
||||
'groups': [],
|
||||
}
|
||||
response = self.client.post(reverse('wagtailadmin_pages:set_privacy', args=(self.private_page.id, )), post_data)
|
||||
|
||||
|
@ -148,7 +263,9 @@ class TestPrivacyIndicators(TestCase, WagtailTestUtils):
|
|||
content="hello",
|
||||
live=True,
|
||||
))
|
||||
PageViewRestriction.objects.create(page=self.private_page, password='password123')
|
||||
PageViewRestriction.objects.create(
|
||||
page=self.private_page, restriction_type='password', password='password123'
|
||||
)
|
||||
|
||||
self.private_child_page = self.private_page.add_child(instance=SimplePage(
|
||||
title="Private child page",
|
||||
|
|
|
@ -24,20 +24,16 @@ def set_privacy(request, page_id):
|
|||
restriction_exists_on_ancestor = False
|
||||
|
||||
if request.method == 'POST':
|
||||
form = PageViewRestrictionForm(request.POST)
|
||||
form = PageViewRestrictionForm(request.POST, instance=restriction)
|
||||
if form.is_valid() and not restriction_exists_on_ancestor:
|
||||
if form.cleaned_data['restriction_type'] == 'none':
|
||||
if form.cleaned_data['restriction_type'] == PageViewRestriction.NONE:
|
||||
# remove any existing restriction
|
||||
if restriction:
|
||||
restriction.delete()
|
||||
else: # restriction_type = 'password'
|
||||
if restriction:
|
||||
restriction.password = form.cleaned_data['password']
|
||||
restriction.save()
|
||||
else:
|
||||
# create a new restriction object
|
||||
PageViewRestriction.objects.create(
|
||||
page=page, password=form.cleaned_data['password'])
|
||||
else:
|
||||
restriction = form.save(commit=False)
|
||||
restriction.page = page
|
||||
form.save()
|
||||
|
||||
return render_modal_workflow(
|
||||
request, None, 'wagtailadmin/page_privacy/set_privacy_done.js', {
|
||||
|
@ -48,9 +44,7 @@ def set_privacy(request, page_id):
|
|||
else: # request is a GET
|
||||
if not restriction_exists_on_ancestor:
|
||||
if restriction:
|
||||
form = PageViewRestrictionForm(initial={
|
||||
'restriction_type': 'password', 'password': restriction.password
|
||||
})
|
||||
form = PageViewRestrictionForm(instance=restriction)
|
||||
else:
|
||||
# no current view restrictions on this page
|
||||
form = PageViewRestrictionForm(initial={
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-10-07 15:33
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0001_initial'),
|
||||
('wagtailcore', '0030_index_on_pagerevision_created_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='pageviewrestriction',
|
||||
name='groups',
|
||||
field=models.ManyToManyField(blank=True, to='auth.Group'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='pageviewrestriction',
|
||||
name='restriction_type',
|
||||
field=models.CharField(choices=[('none', 'Public'), ('login', 'Private, accessible to logged-in users'), ('password', 'Private, accessible with the following password'), ('groups', 'Private, accessible to users in specific groups')], default='password', max_length=20),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pageviewrestriction',
|
||||
name='password',
|
||||
field=models.CharField(blank=True, max_length=255, verbose_name='password'),
|
||||
),
|
||||
]
|
|
@ -1799,8 +1799,25 @@ class PagePermissionTester(object):
|
|||
|
||||
|
||||
class PageViewRestriction(models.Model):
|
||||
page = models.ForeignKey('Page', verbose_name=_('page'), related_name='view_restrictions', on_delete=models.CASCADE)
|
||||
password = models.CharField(verbose_name=_('password'), max_length=255)
|
||||
NONE = 'none'
|
||||
PASSWORD = 'password'
|
||||
GROUPS = 'groups'
|
||||
LOGIN = 'login'
|
||||
|
||||
RESTRICTION_CHOICES = (
|
||||
(NONE, _("Public")),
|
||||
(LOGIN, _("Private, accessible to logged-in users")),
|
||||
(PASSWORD, _("Private, accessible with the following password")),
|
||||
(GROUPS, _("Private, accessible to users in specific groups")),
|
||||
)
|
||||
|
||||
restriction_type = models.CharField(
|
||||
max_length=20, choices=RESTRICTION_CHOICES)
|
||||
page = models.ForeignKey(
|
||||
'Page', verbose_name=_('page'), related_name='view_restrictions', on_delete=models.CASCADE
|
||||
)
|
||||
password = models.CharField(verbose_name=_('password'), max_length=255, blank=True)
|
||||
groups = models.ManyToManyField(Group, blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('page view restriction')
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{% load i18n %}
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>{% trans "Log in" %}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{% trans "Log in" %}</h1>
|
||||
|
||||
{% if form.errors %}
|
||||
<p>{% trans "Your username and password didn't match. Please try again." %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if next and request.user.is_authenticated %}
|
||||
<p>{% trans "Your account doesn't have access to this page. To proceed, please log in with an account that has access." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="{% url 'wagtailcore_login' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Log in" %}" class="button" />
|
||||
<input type="hidden" name="next" value="{{ next }}" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +1,6 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib.auth.models import Group
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.wagtailcore.models import Page, PageViewRestriction
|
||||
|
@ -13,6 +14,10 @@ class TestPagePrivacy(TestCase):
|
|||
self.view_restriction = PageViewRestriction.objects.get(
|
||||
page=self.secret_plans_page)
|
||||
|
||||
self.secret_event_editor_plans_page = Page.objects.get(url_path='/home/secret-event-editor-plans/')
|
||||
self.event_editors_group = Group.objects.get(name='Event editors')
|
||||
self.secret_login_plans_page = Page.objects.get(url_path='/home/secret-login-plans/')
|
||||
|
||||
def test_anonymous_user_must_authenticate(self):
|
||||
response = self.client.get('/secret-plans/')
|
||||
self.assertEqual(response.templates[0].name, 'wagtailcore/password_required.html')
|
||||
|
@ -76,3 +81,34 @@ class TestPagePrivacy(TestCase):
|
|||
# now requests to /secret-plans/ should pass authentication
|
||||
response = self.client.get('/secret-plans/steal-underpants/')
|
||||
self.assertEqual(response.templates[0].name, 'tests/event_page.html')
|
||||
|
||||
def test_group_restriction_with_anonymous_user(self):
|
||||
response = self.client.get('/secret-event-editor-plans/')
|
||||
self.assertRedirects(response, '/_util/login/?next=/secret-event-editor-plans/')
|
||||
|
||||
def test_group_restriction_with_unpermitted_user(self):
|
||||
self.client.login(username='eventmoderator', password='password')
|
||||
response = self.client.get('/secret-event-editor-plans/')
|
||||
self.assertRedirects(response, '/_util/login/?next=/secret-event-editor-plans/')
|
||||
|
||||
def test_group_restriction_with_permitted_user(self):
|
||||
self.client.login(username='eventeditor', password='password')
|
||||
response = self.client.get('/secret-event-editor-plans/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "<title>Secret event editor plans</title>")
|
||||
|
||||
def test_group_restriction_with_superuser(self):
|
||||
self.client.login(username='superuser', password='password')
|
||||
response = self.client.get('/secret-event-editor-plans/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "<title>Secret event editor plans</title>")
|
||||
|
||||
def test_login_restriction_with_anonymous_user(self):
|
||||
response = self.client.get('/secret-login-plans/')
|
||||
self.assertRedirects(response, '/_util/login/?next=/secret-login-plans/')
|
||||
|
||||
def test_login_restriction_with_logged_in_user(self):
|
||||
self.client.login(username='eventmoderator', password='password')
|
||||
response = self.client.get('/secret-login-plans/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "<title>Secret login plans</title>")
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
class TestLoginView(TestCase, WagtailTestUtils):
|
||||
fixtures = ['test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.create_test_user()
|
||||
self.events_index = Page.objects.get(url_path='/home/events/')
|
||||
|
||||
def test_get(self):
|
||||
response = self.client.get(reverse('wagtailcore_login'))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "<h1>Log in</h1>")
|
||||
self.assertNotContains(response, "<p>Your username and password didn't match. Please try again.</p>")
|
||||
|
||||
def test_post_incorrect_password(self):
|
||||
response = self.client.post(reverse('wagtailcore_login'), {
|
||||
'username': 'test@email.com',
|
||||
'password': 'wrongpassword',
|
||||
'next': self.events_index.url,
|
||||
})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "<h1>Log in</h1>")
|
||||
self.assertContains(response, "<p>Your username and password didn't match. Please try again.</p>")
|
||||
|
||||
def test_post_correct_password(self):
|
||||
response = self.client.post(reverse('wagtailcore_login'), {
|
||||
'username': 'test@email.com',
|
||||
'password': 'password',
|
||||
'next': self.events_index.url,
|
||||
})
|
||||
self.assertRedirects(response, self.events_index.url)
|
|
@ -1,6 +1,8 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
from wagtail.wagtailcore import views
|
||||
from wagtail.wagtailcore.utils import WAGTAIL_APPEND_SLASH
|
||||
|
@ -18,9 +20,16 @@ else:
|
|||
serve_pattern = r'^([\w\-/]*)$'
|
||||
|
||||
|
||||
WAGTAIL_FRONTEND_LOGIN_TEMPLATE = getattr(
|
||||
settings, 'WAGTAIL_FRONTEND_LOGIN_TEMPLATE', 'wagtailcore/login.html'
|
||||
)
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^_util/authenticate_with_password/(\d+)/(\d+)/$', views.authenticate_with_password,
|
||||
name='wagtailcore_authenticate_with_password'),
|
||||
url(r'^_util/login/$', auth_views.login, {'template_name': WAGTAIL_FRONTEND_LOGIN_TEMPLATE},
|
||||
name='wagtailcore_login'),
|
||||
|
||||
# Front-end page views are handled through Wagtail's core.views.serve
|
||||
# mechanism
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from wagtail.utils.compat import user_is_authenticated
|
||||
from wagtail.wagtailcore import hooks
|
||||
from wagtail.wagtailcore.models import PageViewRestriction
|
||||
|
||||
|
||||
def require_wagtail_login(next):
|
||||
login_url = getattr(settings, 'WAGTAIL_FRONTEND_LOGIN_URL', reverse('wagtailcore_login'))
|
||||
return redirect_to_login(next, login_url)
|
||||
|
||||
|
||||
@hooks.register('before_serve_page')
|
||||
|
@ -19,9 +28,19 @@ def check_view_restrictions(page, request, serve_args, serve_kwargs):
|
|||
if restrictions:
|
||||
passed_restrictions = request.session.get('passed_page_view_restrictions', [])
|
||||
for restriction in restrictions:
|
||||
if restriction.id not in passed_restrictions:
|
||||
from wagtail.wagtailcore.forms import PasswordPageViewRestrictionForm
|
||||
form = PasswordPageViewRestrictionForm(instance=restriction,
|
||||
initial={'return_url': request.get_full_path()})
|
||||
action_url = reverse('wagtailcore_authenticate_with_password', args=[restriction.id, page.id])
|
||||
return page.serve_password_required_response(request, form, action_url)
|
||||
if restriction.restriction_type == PageViewRestriction.PASSWORD:
|
||||
if restriction.id not in passed_restrictions:
|
||||
from wagtail.wagtailcore.forms import PasswordPageViewRestrictionForm
|
||||
form = PasswordPageViewRestrictionForm(instance=restriction,
|
||||
initial={'return_url': request.get_full_path()})
|
||||
action_url = reverse('wagtailcore_authenticate_with_password', args=[restriction.id, page.id])
|
||||
return page.serve_password_required_response(request, form, action_url)
|
||||
elif restriction.restriction_type == PageViewRestriction.LOGIN:
|
||||
if not user_is_authenticated(request.user):
|
||||
return require_wagtail_login(next=request.get_full_path())
|
||||
elif restriction.restriction_type == PageViewRestriction.GROUPS:
|
||||
if not request.user.is_superuser:
|
||||
current_user_groups = request.user.groups.all()
|
||||
|
||||
if not any(group in current_user_groups for group in restriction.groups.all()):
|
||||
return require_wagtail_login(next=request.get_full_path())
|
||||
|
|
Ładowanie…
Reference in New Issue