feat: expose Role permissions for editing
This commit is contained in:
parent
97e914c2e0
commit
230e2fd1ab
19 changed files with 736 additions and 34 deletions
|
@ -221,3 +221,34 @@ class TestRoleRefs(DataTestCase):
|
|||
self.assertEqual(len(widget.values), 2)
|
||||
self.assertEqual(widget.values[0][1], "Administrator")
|
||||
self.assertEqual(widget.values[1][1], "Blokes")
|
||||
|
||||
|
||||
class TestPermissions(DataTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_db()
|
||||
self.request = testing.DummyRequest(wutta_config=self.config)
|
||||
|
||||
def test_widget_maker(self):
|
||||
|
||||
# no supported permissions
|
||||
permissions = {}
|
||||
typ = mod.Permissions(self.request, permissions)
|
||||
widget = typ.widget_maker()
|
||||
self.assertEqual(len(widget.values), 0)
|
||||
|
||||
# supported permissions are morphed to values
|
||||
permissions = {
|
||||
'widgets': {
|
||||
'label': "Widgets",
|
||||
'perms': {
|
||||
'widgets.polish': {
|
||||
'label': "Polish the widgets",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
typ = mod.Permissions(self.request, permissions)
|
||||
widget = typ.widget_maker()
|
||||
self.assertEqual(len(widget.values), 1)
|
||||
self.assertEqual(widget.values[0], ('widgets.polish', "Polish the widgets"))
|
||||
|
|
|
@ -5,7 +5,7 @@ import deform
|
|||
from pyramid import testing
|
||||
|
||||
from wuttaweb.forms import widgets as mod
|
||||
from wuttaweb.forms.schema import PersonRef, RoleRefs
|
||||
from wuttaweb.forms.schema import PersonRef, RoleRefs, Permissions
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
|
@ -75,3 +75,41 @@ class TestRoleRefsWidget(WebTestCase):
|
|||
html = widget.serialize(field, {admin.uuid, blokes.uuid})
|
||||
self.assertIn(admin.uuid, html)
|
||||
self.assertIn(blokes.uuid, html)
|
||||
|
||||
|
||||
class TestPermissionsWidget(WebTestCase):
|
||||
|
||||
def make_field(self, node, **kwargs):
|
||||
# TODO: not sure why default renderer is in use even though
|
||||
# pyramid_deform was included in setup? but this works..
|
||||
kwargs.setdefault('renderer', deform.Form.default_renderer)
|
||||
return deform.Field(node, **kwargs)
|
||||
|
||||
def test_serialize(self):
|
||||
permissions = {
|
||||
'widgets': {
|
||||
'label': "Widgets",
|
||||
'perms': {
|
||||
'widgets.polish': {
|
||||
'label': "Polish the widgets",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# nb. we let the field construct the widget via our type
|
||||
node = colander.SchemaNode(Permissions(self.request, permissions, session=self.session))
|
||||
field = self.make_field(node)
|
||||
widget = field.widget
|
||||
|
||||
# readonly output does *not* include the perm by default
|
||||
html = widget.serialize(field, set(), readonly=True)
|
||||
self.assertNotIn("Polish the widgets", html)
|
||||
|
||||
# readonly output includes the perm if set
|
||||
html = widget.serialize(field, {'widgets.polish'}, readonly=True)
|
||||
self.assertIn("Polish the widgets", html)
|
||||
|
||||
# editable output always includes the perm
|
||||
html = widget.serialize(field, set())
|
||||
self.assertIn("Polish the widgets", html)
|
||||
|
|
|
@ -7,6 +7,7 @@ from pyramid import testing
|
|||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
from wuttaweb import auth as mod
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
class TestLoginUser(TestCase):
|
||||
|
@ -143,3 +144,26 @@ class TestWuttaSecurityPolicy(TestCase):
|
|||
self.assertFalse(self.policy.permits(self.request, None, 'some-root-perm'))
|
||||
self.request.is_root = True
|
||||
self.assertTrue(self.policy.permits(self.request, None, 'some-root-perm'))
|
||||
|
||||
|
||||
class TestAddPermissionGroup(WebTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
permissions = self.pyramid_config.get_settings().get('wutta_permissions', {})
|
||||
self.assertNotIn('widgets', permissions)
|
||||
self.pyramid_config.add_wutta_permission_group('widgets')
|
||||
permissions = self.pyramid_config.get_settings().get('wutta_permissions', {})
|
||||
self.assertIn('widgets', permissions)
|
||||
self.assertEqual(permissions['widgets']['label'], "Widgets")
|
||||
|
||||
|
||||
class TestAddPermission(WebTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
permissions = self.pyramid_config.get_settings().get('wutta_permissions', {})
|
||||
self.assertNotIn('widgets', permissions)
|
||||
self.pyramid_config.add_wutta_permission('widgets', 'widgets.polish')
|
||||
permissions = self.pyramid_config.get_settings().get('wutta_permissions', {})
|
||||
self.assertIn('widgets', permissions)
|
||||
self.assertEqual(permissions['widgets']['label'], "Widgets")
|
||||
self.assertIn('widgets.polish', permissions['widgets']['perms'])
|
||||
|
|
|
@ -56,10 +56,13 @@ class WebTestCase(DataTestCase):
|
|||
# init web
|
||||
self.pyramid_config.include('pyramid_deform')
|
||||
self.pyramid_config.include('pyramid_mako')
|
||||
self.pyramid_config.include('wuttaweb.static')
|
||||
self.pyramid_config.include('wuttaweb.views.essential')
|
||||
self.pyramid_config.add_directive('add_wutta_permission_group',
|
||||
'wuttaweb.auth.add_permission_group')
|
||||
self.pyramid_config.add_directive('add_wutta_permission',
|
||||
'wuttaweb.auth.add_permission')
|
||||
self.pyramid_config.add_subscriber('wuttaweb.subscribers.before_render',
|
||||
'pyramid.events.BeforeRender')
|
||||
self.pyramid_config.include('wuttaweb.static')
|
||||
|
||||
# setup new request w/ anonymous user
|
||||
event = MagicMock(request=self.request)
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from pyramid import testing
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
class TestIncludeMe(TestCase):
|
||||
class TestIncludeMe(WebTestCase):
|
||||
|
||||
def test_basic(self):
|
||||
with testing.testConfig() as pyramid_config:
|
||||
|
||||
# just ensure no error happens when included..
|
||||
pyramid_config.include('wuttaweb.views')
|
||||
# just ensure no error happens when included..
|
||||
self.pyramid_config.include('wuttaweb.views')
|
||||
|
|
|
@ -162,6 +162,24 @@ class TestMasterView(WebTestCase):
|
|||
model_class=MyModel):
|
||||
self.assertEqual(master.MasterView.get_route_prefix(), 'trucks')
|
||||
|
||||
def test_get_permission_prefix(self):
|
||||
|
||||
# error by default (since no model class)
|
||||
self.assertRaises(AttributeError, master.MasterView.get_permission_prefix)
|
||||
|
||||
# subclass may specify permission prefix
|
||||
with patch.object(master.MasterView, 'permission_prefix', new='widgets', create=True):
|
||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'widgets')
|
||||
|
||||
# subclass may specify route prefix
|
||||
with patch.object(master.MasterView, 'route_prefix', new='widgets', create=True):
|
||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'widgets')
|
||||
|
||||
# or it may specify model class
|
||||
Truck = MagicMock(__name__='Truck')
|
||||
with patch.object(master.MasterView, 'model_class', new=Truck, create=True):
|
||||
self.assertEqual(master.MasterView.get_permission_prefix(), 'trucks')
|
||||
|
||||
def test_get_url_prefix(self):
|
||||
|
||||
# error by default (since no model class)
|
||||
|
@ -315,6 +333,9 @@ class TestMasterView(WebTestCase):
|
|||
##############################
|
||||
|
||||
def test_render_to_response(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('appinfo', '/appinfo/')
|
||||
|
||||
def widgets(request): return {}
|
||||
self.pyramid_config.add_route('widgets', '/widgets/')
|
||||
|
@ -539,28 +560,38 @@ class TestMasterView(WebTestCase):
|
|||
##############################
|
||||
|
||||
def test_index(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('settings.create', '/settings/new')
|
||||
self.pyramid_config.add_route('settings.view', '/settings/{name}')
|
||||
self.pyramid_config.add_route('settings.edit', '/settings/{name}/edit')
|
||||
self.pyramid_config.add_route('settings.delete', '/settings/{name}/delete')
|
||||
|
||||
# sanity/coverage check using /settings/
|
||||
master.MasterView.model_name = 'Setting'
|
||||
master.MasterView.model_key = 'name'
|
||||
master.MasterView.grid_columns = ['name', 'value']
|
||||
view = master.MasterView(self.request)
|
||||
response = view.index()
|
||||
# then again with data, to include view action url
|
||||
data = [{'name': 'foo', 'value': 'bar'}]
|
||||
with patch.object(view, 'get_grid_data', return_value=data):
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting',
|
||||
model_key='name',
|
||||
get_index_url=MagicMock(return_value='/settings/'),
|
||||
grid_columns=['name', 'value']):
|
||||
view = master.MasterView(self.request)
|
||||
response = view.index()
|
||||
del master.MasterView.model_name
|
||||
del master.MasterView.model_key
|
||||
del master.MasterView.grid_columns
|
||||
|
||||
# then again with data, to include view action url
|
||||
data = [{'name': 'foo', 'value': 'bar'}]
|
||||
with patch.object(view, 'get_grid_data', return_value=data):
|
||||
response = view.index()
|
||||
|
||||
def test_create(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('settings.view', '/settings/{name}')
|
||||
model = self.app.model
|
||||
|
||||
# sanity/coverage check using /settings/new
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting',
|
||||
model_key='name',
|
||||
get_index_url=MagicMock(return_value='/settings/'),
|
||||
form_fields=['name', 'value']):
|
||||
view = master.MasterView(self.request)
|
||||
|
||||
|
@ -604,6 +635,11 @@ class TestMasterView(WebTestCase):
|
|||
self.assertEqual(self.app.get_setting(self.session, 'foo.bar'), 'fraggle')
|
||||
|
||||
def test_view(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('settings.create', '/settings/new')
|
||||
self.pyramid_config.add_route('settings.edit', '/settings/{name}/edit')
|
||||
self.pyramid_config.add_route('settings.delete', '/settings/{name}/delete')
|
||||
|
||||
# sanity/coverage check using /settings/XXX
|
||||
setting = {'name': 'foo.bar', 'value': 'baz'}
|
||||
|
@ -611,6 +647,7 @@ class TestMasterView(WebTestCase):
|
|||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting',
|
||||
model_key='name',
|
||||
get_index_url=MagicMock(return_value='/settings/'),
|
||||
grid_columns=['name', 'value'],
|
||||
form_fields=['name', 'value']):
|
||||
view = master.MasterView(self.request)
|
||||
|
@ -618,6 +655,11 @@ class TestMasterView(WebTestCase):
|
|||
response = view.view()
|
||||
|
||||
def test_edit(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('settings.create', '/settings/new')
|
||||
self.pyramid_config.add_route('settings.view', '/settings/{name}')
|
||||
self.pyramid_config.add_route('settings.delete', '/settings/{name}/delete')
|
||||
model = self.app.model
|
||||
self.app.save_setting(self.session, 'foo.bar', 'frazzle')
|
||||
self.session.commit()
|
||||
|
@ -634,6 +676,7 @@ class TestMasterView(WebTestCase):
|
|||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting',
|
||||
model_key='name',
|
||||
get_index_url=MagicMock(return_value='/settings/'),
|
||||
form_fields=['name', 'value']):
|
||||
view = master.MasterView(self.request)
|
||||
with patch.object(view, 'get_instance', new=get_instance):
|
||||
|
@ -675,6 +718,11 @@ class TestMasterView(WebTestCase):
|
|||
self.assertEqual(self.app.get_setting(self.session, 'foo.bar'), 'froogle')
|
||||
|
||||
def test_delete(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
self.pyramid_config.add_route('settings.create', '/settings/new')
|
||||
self.pyramid_config.add_route('settings.view', '/settings/{name}')
|
||||
self.pyramid_config.add_route('settings.edit', '/settings/{name}/edit')
|
||||
model = self.app.model
|
||||
self.app.save_setting(self.session, 'foo.bar', 'frazzle')
|
||||
self.session.commit()
|
||||
|
@ -692,6 +740,7 @@ class TestMasterView(WebTestCase):
|
|||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting',
|
||||
model_key='name',
|
||||
get_index_url=MagicMock(return_value='/settings/'),
|
||||
form_fields=['name', 'value']):
|
||||
view = master.MasterView(self.request)
|
||||
with patch.object(view, 'get_instance', new=get_instance):
|
||||
|
@ -730,6 +779,8 @@ class TestMasterView(WebTestCase):
|
|||
self.assertEqual(self.session.query(model.Setting).count(), 0)
|
||||
|
||||
def test_configure(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
model = self.app.model
|
||||
|
||||
# mock settings
|
||||
|
@ -750,6 +801,7 @@ class TestMasterView(WebTestCase):
|
|||
route_prefix='appinfo',
|
||||
template_prefix='/appinfo',
|
||||
creatable=False,
|
||||
get_index_url=MagicMock(return_value='/appinfo/'),
|
||||
configure_get_simple_settings=MagicMock(return_value=settings)):
|
||||
|
||||
# get the form page
|
||||
|
|
|
@ -15,6 +15,9 @@ class TestRoleView(WebTestCase):
|
|||
def make_view(self):
|
||||
return mod.RoleView(self.request)
|
||||
|
||||
def test_includeme(self):
|
||||
self.pyramid_config.include('wuttaweb.views.roles')
|
||||
|
||||
def test_get_query(self):
|
||||
view = self.make_view()
|
||||
query = view.get_query(session=self.session)
|
||||
|
@ -30,8 +33,9 @@ class TestRoleView(WebTestCase):
|
|||
|
||||
def test_configure_form(self):
|
||||
model = self.app.model
|
||||
role = model.Role(name="Foo")
|
||||
view = self.make_view()
|
||||
form = view.make_form(model_class=model.Person)
|
||||
form = view.make_form(model_instance=role)
|
||||
self.assertNotIn('name', form.validators)
|
||||
view.configure_form(form)
|
||||
self.assertIsNotNone(form.validators['name'])
|
||||
|
@ -55,3 +59,132 @@ class TestRoleView(WebTestCase):
|
|||
self.request.matchdict = {'uuid': role.uuid}
|
||||
node = colander.SchemaNode(colander.String(), name='name')
|
||||
self.assertIsNone(view.unique_name(node, 'Foo'))
|
||||
|
||||
def get_permissions(self):
|
||||
return {
|
||||
'widgets': {
|
||||
'label': "Widgets",
|
||||
'perms': {
|
||||
'widgets.list': {
|
||||
'label': "List widgets",
|
||||
},
|
||||
'widgets.polish': {
|
||||
'label': "Polish the widgets",
|
||||
},
|
||||
'widgets.view': {
|
||||
'label': "View widget",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
def test_get_available_permissions(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
blokes = model.Role(name="Blokes")
|
||||
auth.grant_permission(blokes, 'widgets.list')
|
||||
self.session.add(blokes)
|
||||
barney = model.User(username='barney')
|
||||
barney.roles.append(blokes)
|
||||
self.session.add(barney)
|
||||
self.session.commit()
|
||||
view = self.make_view()
|
||||
all_perms = self.get_permissions()
|
||||
self.request.registry.settings['wutta_permissions'] = all_perms
|
||||
|
||||
def has_perm(perm):
|
||||
if perm == 'widgets.list':
|
||||
return True
|
||||
return False
|
||||
|
||||
with patch.object(self.request, 'has_perm', new=has_perm, create=True):
|
||||
|
||||
# sanity check; current request has 1 perm
|
||||
self.assertTrue(self.request.has_perm('widgets.list'))
|
||||
self.assertFalse(self.request.has_perm('widgets.polish'))
|
||||
self.assertFalse(self.request.has_perm('widgets.view'))
|
||||
|
||||
# when editing, user sees only the 1 perm
|
||||
with patch.object(view, 'editing', new=True):
|
||||
perms = view.get_available_permissions()
|
||||
self.assertEqual(list(perms), ['widgets'])
|
||||
self.assertEqual(list(perms['widgets']['perms']), ['widgets.list'])
|
||||
|
||||
# but when viewing, same user sees all perms
|
||||
with patch.object(view, 'viewing', new=True):
|
||||
perms = view.get_available_permissions()
|
||||
self.assertEqual(list(perms), ['widgets'])
|
||||
self.assertEqual(list(perms['widgets']['perms']),
|
||||
['widgets.list', 'widgets.polish', 'widgets.view'])
|
||||
|
||||
# also, when admin user is editing, sees all perms
|
||||
self.request.is_admin = True
|
||||
with patch.object(view, 'editing', new=True):
|
||||
perms = view.get_available_permissions()
|
||||
self.assertEqual(list(perms), ['widgets'])
|
||||
self.assertEqual(list(perms['widgets']['perms']),
|
||||
['widgets.list', 'widgets.polish', 'widgets.view'])
|
||||
|
||||
def test_objectify(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
barney = model.User(username='barney')
|
||||
barney.roles.append(blokes)
|
||||
self.session.add(barney)
|
||||
self.session.commit()
|
||||
view = self.make_view()
|
||||
permissions = self.get_permissions()
|
||||
|
||||
# sanity check, role has just 1 perm
|
||||
auth.grant_permission(blokes, 'widgets.list')
|
||||
self.session.commit()
|
||||
self.assertEqual(blokes.permissions, ['widgets.list'])
|
||||
|
||||
# form can update role perms
|
||||
view.editing = True
|
||||
self.request.matchdict = {'uuid': blokes.uuid}
|
||||
with patch.object(view, 'get_available_permissions', return_value=permissions):
|
||||
form = view.make_model_form(model_instance=blokes)
|
||||
form.validated = {'name': 'Blokes',
|
||||
'permissions': {'widgets.list', 'widgets.polish', 'widgets.view'}}
|
||||
role = view.objectify(form)
|
||||
self.session.commit()
|
||||
self.assertIs(role, blokes)
|
||||
self.assertEqual(blokes.permissions, ['widgets.list', 'widgets.polish', 'widgets.view'])
|
||||
|
||||
def test_update_permissions(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
blokes = model.Role(name="Blokes")
|
||||
auth.grant_permission(blokes, 'widgets.list')
|
||||
self.session.add(blokes)
|
||||
barney = model.User(username='barney')
|
||||
barney.roles.append(blokes)
|
||||
self.session.add(barney)
|
||||
self.session.commit()
|
||||
view = self.make_view()
|
||||
permissions = self.get_permissions()
|
||||
|
||||
with patch.object(view, 'get_available_permissions', return_value=permissions):
|
||||
|
||||
# no error if data is missing perms
|
||||
form = view.make_model_form(model_instance=blokes)
|
||||
form.validated = {'name': 'BloX'}
|
||||
role = view.objectify(form)
|
||||
self.session.commit()
|
||||
self.assertIs(role, blokes)
|
||||
self.assertEqual(blokes.name, 'BloX')
|
||||
|
||||
# sanity check, role has just 1 perm
|
||||
self.assertEqual(blokes.permissions, ['widgets.list'])
|
||||
|
||||
# role perms are updated
|
||||
form = view.make_model_form(model_instance=blokes)
|
||||
form.validated = {'name': 'Blokes',
|
||||
'permissions': {'widgets.polish', 'widgets.view'}}
|
||||
role = view.objectify(form)
|
||||
self.session.commit()
|
||||
self.assertIs(role, blokes)
|
||||
self.assertEqual(blokes.permissions, ['widgets.polish', 'widgets.view'])
|
||||
|
|
|
@ -10,6 +10,10 @@ from tests.util import WebTestCase
|
|||
|
||||
class TestAppInfoView(WebTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.setup_web()
|
||||
self.pyramid_config.include('wuttaweb.views.essential')
|
||||
|
||||
def make_view(self):
|
||||
return settings.AppInfoView(self.request)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue