feat: add permission checks for menus, view routes
This commit is contained in:
parent
675b51cac2
commit
e3942ce65e
|
@ -272,9 +272,8 @@ class MenuHandler(GenericHandler):
|
|||
current user.
|
||||
"""
|
||||
perm = item.get('perm')
|
||||
# TODO
|
||||
# if perm:
|
||||
# return request.has_perm(perm)
|
||||
if perm:
|
||||
return request.has_perm(perm)
|
||||
return True
|
||||
|
||||
def _mark_allowed(self, request, menus):
|
||||
|
|
|
@ -139,6 +139,16 @@ def new_request_set_user(
|
|||
pyramid_config.add_subscriber('wuttaweb.subscribers.new_request_set_user',
|
||||
'pyramid.events.NewRequest')
|
||||
|
||||
You may wish to "supplement" this hook by registering your own
|
||||
custom hook and then invoking this one as needed. You can then
|
||||
pass certain params to override only parts of the logic:
|
||||
|
||||
:param user_getter: Optional getter function to retrieve the user
|
||||
from database, instead of :func:`default_user_getter()`.
|
||||
|
||||
:param db_session: Optional :term:`db session` to use,
|
||||
instead of :class:`wuttaweb.db.Session`.
|
||||
|
||||
This will add to the request object:
|
||||
|
||||
.. attribute:: request.user
|
||||
|
@ -158,19 +168,36 @@ def new_request_set_user(
|
|||
privileges. This is only possible if :attr:`request.is_admin`
|
||||
is also true.
|
||||
|
||||
You may wish to "supplement" this hook by registering your own
|
||||
custom hook and then invoking this one as needed. You can then
|
||||
pass certain params to override only parts of the logic:
|
||||
.. attribute:: request.user_permissions
|
||||
|
||||
:param user_getter: Optional getter function to retrieve the user
|
||||
from database, instead of :func:`default_user_getter()`.
|
||||
The ``set`` of permission names which are granted to the
|
||||
current user.
|
||||
|
||||
This set is obtained by calling
|
||||
:meth:`~wuttjamaican:wuttjamaican.auth.AuthHandler.get_permissions()`.
|
||||
|
||||
.. function:: request.has_perm(name)
|
||||
|
||||
Shortcut to check if current user has the given permission::
|
||||
|
||||
if not request.has_perm('users.edit'):
|
||||
raise self.forbidden()
|
||||
|
||||
.. function:: request.has_any_perm(*names)
|
||||
|
||||
Shortcut to check if current user has any of the given
|
||||
permissions::
|
||||
|
||||
if request.has_any_perm('users.list', 'users.view'):
|
||||
return "can either list or view"
|
||||
else:
|
||||
raise self.forbidden()
|
||||
|
||||
:param db_session: Optional :term:`db session` to use,
|
||||
instead of :class:`wuttaweb.db.Session`.
|
||||
"""
|
||||
request = event.request
|
||||
config = request.registry.settings['wutta_config']
|
||||
app = config.get_app()
|
||||
auth = app.get_auth_handler()
|
||||
|
||||
# request.user
|
||||
if db_session:
|
||||
|
@ -179,7 +206,6 @@ def new_request_set_user(
|
|||
|
||||
# request.is_admin
|
||||
def is_admin(request):
|
||||
auth = app.get_auth_handler()
|
||||
return auth.user_is_admin(request.user)
|
||||
request.set_property(is_admin, reify=True)
|
||||
|
||||
|
@ -191,6 +217,29 @@ def new_request_set_user(
|
|||
return False
|
||||
request.set_property(is_root, reify=True)
|
||||
|
||||
# request.user_permissions
|
||||
def user_permissions(request):
|
||||
session = db_session or Session()
|
||||
return auth.get_permissions(session, request.user)
|
||||
request.set_property(user_permissions, reify=True)
|
||||
|
||||
# request.has_perm()
|
||||
def has_perm(name):
|
||||
if request.is_root:
|
||||
return True
|
||||
if name in request.user_permissions:
|
||||
return True
|
||||
return False
|
||||
request.has_perm = has_perm
|
||||
|
||||
# request.has_any_perm()
|
||||
def has_any_perm(*names):
|
||||
for name in names:
|
||||
if request.has_perm(name):
|
||||
return True
|
||||
return False
|
||||
request.has_any_perm = has_any_perm
|
||||
|
||||
|
||||
def before_render(event):
|
||||
"""
|
||||
|
|
|
@ -222,7 +222,7 @@
|
|||
% else:
|
||||
<h1 class="title">${index_title}</h1>
|
||||
% endif
|
||||
% if master and master.creatable and not master.creating:
|
||||
% if master and master.creatable and not master.creating and master.has_perm('create'):
|
||||
<wutta-button once type="is-primary"
|
||||
tag="a" href="${url(f'{route_prefix}.create')}"
|
||||
icon-left="plus"
|
||||
|
@ -235,8 +235,7 @@
|
|||
|
||||
<div class="level-right">
|
||||
|
||||
## TODO
|
||||
% if master and master.configurable and not master.configuring:
|
||||
% if master and master.configurable and not master.configuring and master.has_perm('configure'):
|
||||
<div class="level-item">
|
||||
<wutta-button once type="is-primary"
|
||||
tag="a" href="${url(f'{route_prefix}.configure')}"
|
||||
|
|
|
@ -102,10 +102,50 @@ class CommonView(View):
|
|||
# assign admin role
|
||||
admin = auth.get_role_administrator(session)
|
||||
user.roles.append(admin)
|
||||
admin.notes = ("users in this role may \"become root\".\n\n"
|
||||
"it's recommended not to grant other perms to this role.")
|
||||
|
||||
# ensure all built-in roles exist
|
||||
auth.get_role_authenticated(session)
|
||||
auth.get_role_anonymous(session)
|
||||
# initialize built-in roles
|
||||
authed = auth.get_role_authenticated(session)
|
||||
authed.notes = ("this role represents any user who *is* logged in.\n\n"
|
||||
"you may grant any perms you like to it.")
|
||||
anon = auth.get_role_anonymous(session)
|
||||
anon.notes = ("this role represents any user who is *not* logged in.\n\n"
|
||||
"you may grant any perms you like to it.")
|
||||
|
||||
# also make "Site Admin" role
|
||||
site_admin_perms = [
|
||||
'appinfo.list',
|
||||
'appinfo.configure',
|
||||
'people.list',
|
||||
'people.create',
|
||||
'people.view',
|
||||
'people.edit',
|
||||
'people.delete',
|
||||
'roles.list',
|
||||
'roles.create',
|
||||
'roles.view',
|
||||
'roles.edit',
|
||||
'roles.edit_builtin',
|
||||
'roles.delete',
|
||||
'settings.list',
|
||||
'settings.create',
|
||||
'settings.view',
|
||||
'settings.edit',
|
||||
'settings.delete',
|
||||
'users.list',
|
||||
'users.create',
|
||||
'users.view',
|
||||
'users.edit',
|
||||
'users.delete',
|
||||
]
|
||||
admin2 = model.Role(name="Site Admin")
|
||||
admin2.notes = ("this is the \"daily driver\" admin role.\n\n"
|
||||
"you may grant any perms you like to it.")
|
||||
session.add(admin2)
|
||||
user.roles.append(admin2)
|
||||
for perm in site_admin_perms:
|
||||
auth.grant_permission(admin2, perm)
|
||||
|
||||
# maybe make person
|
||||
if data['first_name'] or data['last_name']:
|
||||
|
|
|
@ -198,6 +198,8 @@ class MasterView(View):
|
|||
i.e. it should have an :meth:`edit()` view. Default value is
|
||||
``True``.
|
||||
|
||||
See also :meth:`is_editable()`.
|
||||
|
||||
.. attribute:: deletable
|
||||
|
||||
Boolean indicating whether the view model supports "deleting" -
|
||||
|
@ -802,6 +804,43 @@ class MasterView(View):
|
|||
# support methods
|
||||
##############################
|
||||
|
||||
def has_perm(self, name):
|
||||
"""
|
||||
Shortcut to check if current user has the given permission.
|
||||
|
||||
This will automatically add the :attr:`permission_prefix` to
|
||||
``name`` before passing it on to
|
||||
:func:`~wuttaweb.subscribers.request.has_perm()`.
|
||||
|
||||
For instance within the
|
||||
:class:`~wuttaweb.views.users.UserView` these give the same
|
||||
result::
|
||||
|
||||
self.request.has_perm('users.edit')
|
||||
|
||||
self.has_perm('edit')
|
||||
|
||||
So this shortcut only applies to permissions defined for the
|
||||
current master view. The first example above must still be
|
||||
used to check for "foreign" permissions (i.e. any needing a
|
||||
different prefix).
|
||||
"""
|
||||
permission_prefix = self.get_permission_prefix()
|
||||
return self.request.has_perm(f'{permission_prefix}.{name}')
|
||||
|
||||
def has_any_perm(self, *names):
|
||||
"""
|
||||
Shortcut to check if current user has any of the given
|
||||
permissions.
|
||||
|
||||
This calls :meth:`has_perm()` until one returns ``True``. If
|
||||
none do, returns ``False``.
|
||||
"""
|
||||
for name in names:
|
||||
if self.has_perm(name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def render_to_response(self, template, context):
|
||||
"""
|
||||
Locate and render an appropriate template, with the given
|
||||
|
@ -937,15 +976,15 @@ class MasterView(View):
|
|||
|
||||
# TODO: should split this off into index_get_grid_actions() ?
|
||||
|
||||
if self.viewable:
|
||||
if self.viewable and self.has_perm('view'):
|
||||
actions.append(self.make_grid_action('view', icon='eye',
|
||||
url=self.get_action_url_view))
|
||||
|
||||
if self.editable:
|
||||
if self.editable and self.has_perm('edit'):
|
||||
actions.append(self.make_grid_action('edit', icon='edit',
|
||||
url=self.get_action_url_edit))
|
||||
|
||||
if self.deletable:
|
||||
if self.deletable and self.has_perm('delete'):
|
||||
actions.append(self.make_grid_action('delete', icon='trash',
|
||||
url=self.get_action_url_delete,
|
||||
link_class='has-text-danger'))
|
||||
|
@ -1137,14 +1176,19 @@ class MasterView(View):
|
|||
|
||||
def get_action_url_edit(self, obj, i):
|
||||
"""
|
||||
Returns the "edit" grid action URL for the given object.
|
||||
Returns the "edit" grid action URL for the given object, if
|
||||
applicable.
|
||||
|
||||
Most typically this is like ``/widgets/XXX/edit`` where
|
||||
``XXX`` represents the object's key/ID.
|
||||
|
||||
Calls :meth:`get_action_url()` under the hood.
|
||||
This first calls :meth:`is_editable()` and if that is false,
|
||||
this method will return ``None``.
|
||||
|
||||
Calls :meth:`get_action_url()` to generate the true URL.
|
||||
"""
|
||||
return self.get_action_url('edit', obj)
|
||||
if self.is_editable(obj):
|
||||
return self.get_action_url('edit', obj)
|
||||
|
||||
def get_action_url_delete(self, obj, i):
|
||||
"""
|
||||
|
@ -1162,6 +1206,19 @@ class MasterView(View):
|
|||
if self.is_deletable(obj):
|
||||
return self.get_action_url('delete', obj)
|
||||
|
||||
def is_editable(self, obj):
|
||||
"""
|
||||
Returns a boolean indicating whether "edit" should be allowed
|
||||
for the given model instance (and for current user).
|
||||
|
||||
By default this always return ``True``; subclass can override
|
||||
if needed.
|
||||
|
||||
Note that the use of this method implies :attr:`editable` is
|
||||
true, so the method does not need to check that flag.
|
||||
"""
|
||||
return True
|
||||
|
||||
def is_deletable(self, obj):
|
||||
"""
|
||||
Returns a boolean indicating whether "delete" should be
|
||||
|
@ -1634,7 +1691,8 @@ class MasterView(View):
|
|||
if cls.listable:
|
||||
config.add_route(route_prefix, f'{url_prefix}/')
|
||||
config.add_view(cls, attr='index',
|
||||
route_name=route_prefix)
|
||||
route_name=route_prefix,
|
||||
permission=f'{permission_prefix}.list')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.list',
|
||||
f"Browse / search {model_title_plural}")
|
||||
|
@ -1644,7 +1702,8 @@ class MasterView(View):
|
|||
config.add_route(f'{route_prefix}.create',
|
||||
f'{url_prefix}/new')
|
||||
config.add_view(cls, attr='create',
|
||||
route_name=f'{route_prefix}.create')
|
||||
route_name=f'{route_prefix}.create',
|
||||
permission=f'{permission_prefix}.create')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.create',
|
||||
f"Create new {model_title}")
|
||||
|
@ -1654,7 +1713,8 @@ class MasterView(View):
|
|||
instance_url_prefix = cls.get_instance_url_prefix()
|
||||
config.add_route(f'{route_prefix}.view', instance_url_prefix)
|
||||
config.add_view(cls, attr='view',
|
||||
route_name=f'{route_prefix}.view')
|
||||
route_name=f'{route_prefix}.view',
|
||||
permission=f'{permission_prefix}.view')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.view',
|
||||
f"View {model_title}")
|
||||
|
@ -1665,7 +1725,8 @@ class MasterView(View):
|
|||
config.add_route(f'{route_prefix}.edit',
|
||||
f'{instance_url_prefix}/edit')
|
||||
config.add_view(cls, attr='edit',
|
||||
route_name=f'{route_prefix}.edit')
|
||||
route_name=f'{route_prefix}.edit',
|
||||
permission=f'{permission_prefix}.edit')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.edit',
|
||||
f"Edit {model_title}")
|
||||
|
@ -1676,7 +1737,8 @@ class MasterView(View):
|
|||
config.add_route(f'{route_prefix}.delete',
|
||||
f'{instance_url_prefix}/delete')
|
||||
config.add_view(cls, attr='delete',
|
||||
route_name=f'{route_prefix}.delete')
|
||||
route_name=f'{route_prefix}.delete',
|
||||
permission=f'{permission_prefix}.delete')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.delete',
|
||||
f"Delete {model_title}")
|
||||
|
@ -1686,7 +1748,8 @@ class MasterView(View):
|
|||
config.add_route(f'{route_prefix}.configure',
|
||||
f'{url_prefix}/configure')
|
||||
config.add_view(cls, attr='configure',
|
||||
route_name=f'{route_prefix}.configure')
|
||||
route_name=f'{route_prefix}.configure',
|
||||
permission=f'{permission_prefix}.configure')
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.configure',
|
||||
f"Configure {model_title_plural}")
|
||||
|
|
|
@ -69,6 +69,22 @@ class RoleView(MasterView):
|
|||
# notes
|
||||
g.set_renderer('notes', self.grid_render_notes)
|
||||
|
||||
def is_editable(self, role):
|
||||
""" """
|
||||
session = self.app.get_session(role)
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
# only "root" can edit admin role
|
||||
if role is auth.get_role_administrator(session):
|
||||
return self.request.is_root
|
||||
|
||||
# other built-in roles require special perm
|
||||
if role in (auth.get_role_authenticated(session),
|
||||
auth.get_role_anonymous(session)):
|
||||
return self.has_perm('edit_builtin')
|
||||
|
||||
return True
|
||||
|
||||
def is_deletable(self, role):
|
||||
""" """
|
||||
session = self.app.get_session(role)
|
||||
|
@ -228,6 +244,21 @@ class RoleView(MasterView):
|
|||
else:
|
||||
auth.revoke_permission(role, pkey)
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._defaults(config)
|
||||
cls._role_defaults(config)
|
||||
|
||||
@classmethod
|
||||
def _role_defaults(cls, config):
|
||||
permission_prefix = cls.get_permission_prefix()
|
||||
model_title_plural = cls.get_model_title_plural()
|
||||
|
||||
# perm to edit built-in roles
|
||||
config.add_wutta_permission(permission_prefix,
|
||||
f'{permission_prefix}.edit_builtin',
|
||||
f"Edit the Built-in {model_title_plural}")
|
||||
|
||||
|
||||
def defaults(config, **kwargs):
|
||||
base = globals()
|
||||
|
|
|
@ -3,20 +3,15 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
|
||||
from pyramid import testing
|
||||
|
||||
from wuttaweb import menus as mod
|
||||
from tests.util import WebTestCase
|
||||
|
||||
|
||||
class TestMenuHandler(TestCase):
|
||||
class TestMenuHandler(WebTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.config = WuttaConfig()
|
||||
self.app = self.config.get_app()
|
||||
self.setup_web()
|
||||
self.handler = mod.MenuHandler(self.config)
|
||||
self.request = testing.DummyRequest()
|
||||
|
||||
def test_make_admin_menu(self):
|
||||
menus = self.handler.make_admin_menu(self.request)
|
||||
|
@ -27,7 +22,27 @@ class TestMenuHandler(TestCase):
|
|||
self.assertIsInstance(menus, list)
|
||||
|
||||
def test_is_allowed(self):
|
||||
# TODO: this should test auth/perm handling
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
# user with perms
|
||||
barney = model.User(username='barney')
|
||||
self.session.add(barney)
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
barney.roles.append(blokes)
|
||||
auth.grant_permission(blokes, 'appinfo.list')
|
||||
self.request.user = barney
|
||||
|
||||
# perm not granted to user
|
||||
item = {'perm': 'appinfo.configure'}
|
||||
self.assertFalse(self.handler._is_allowed(self.request, item))
|
||||
|
||||
# perm *is* granted to user
|
||||
item = {'perm': 'appinfo.list'}
|
||||
self.assertTrue(self.handler._is_allowed(self.request, item))
|
||||
|
||||
# perm not required
|
||||
item = {}
|
||||
self.assertTrue(self.handler._is_allowed(self.request, item))
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import json
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from wuttjamaican.conf import WuttaConfig
|
||||
|
||||
|
@ -210,6 +210,137 @@ class TestNewRequestSetUser(TestCase):
|
|||
self.assertTrue(self.request.is_admin)
|
||||
self.assertTrue(self.request.is_root)
|
||||
|
||||
def test_user_permissions(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
event = MagicMock(request=self.request)
|
||||
|
||||
# anonymous user
|
||||
self.assertFalse(hasattr(self.request, 'user_permissions'))
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertEqual(self.request.user_permissions, set())
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
|
||||
# add user to role with perms
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
auth.grant_permission(blokes, 'appinfo.list')
|
||||
self.user.roles.append(blokes)
|
||||
self.session.commit()
|
||||
|
||||
# authenticated user, with perms
|
||||
self.request.user = self.user
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertEqual(self.request.user_permissions, {'appinfo.list'})
|
||||
|
||||
def test_has_perm(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
event = MagicMock(request=self.request)
|
||||
|
||||
# anonymous user
|
||||
self.assertFalse(hasattr(self.request, 'has_perm'))
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertFalse(self.request.has_perm('appinfo.list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
|
||||
# add user to role with perms
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
auth.grant_permission(blokes, 'appinfo.list')
|
||||
self.user.roles.append(blokes)
|
||||
self.session.commit()
|
||||
|
||||
# authenticated user, with perms
|
||||
self.request.user = self.user
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertTrue(self.request.has_perm('appinfo.list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
|
||||
# drop user from role, no more perms
|
||||
self.user.roles.remove(blokes)
|
||||
self.session.commit()
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertFalse(self.request.has_perm('appinfo.list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
del self.request.is_admin
|
||||
del self.request.is_root
|
||||
|
||||
# root user always has perms
|
||||
admin = auth.get_role_administrator(self.session)
|
||||
self.user.roles.append(admin)
|
||||
self.session.commit()
|
||||
self.request.session['is_root'] = True
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertTrue(self.request.has_perm('appinfo.list'))
|
||||
|
||||
def test_has_any_perm(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
event = MagicMock(request=self.request)
|
||||
|
||||
# anonymous user
|
||||
self.assertFalse(hasattr(self.request, 'has_any_perm'))
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertFalse(self.request.has_any_perm('appinfo.list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
|
||||
# add user to role with perms
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
auth.grant_permission(blokes, 'appinfo.list')
|
||||
self.user.roles.append(blokes)
|
||||
self.session.commit()
|
||||
|
||||
# authenticated user, with perms
|
||||
self.request.user = self.user
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertTrue(self.request.has_any_perm('appinfo.list', 'appinfo.view'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
|
||||
# drop user from role, no more perms
|
||||
self.user.roles.remove(blokes)
|
||||
self.session.commit()
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertFalse(self.request.has_any_perm('appinfo.list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
del self.request.has_perm
|
||||
del self.request.has_any_perm
|
||||
del self.request.is_admin
|
||||
del self.request.is_root
|
||||
|
||||
# root user always has perms
|
||||
admin = auth.get_role_administrator(self.session)
|
||||
self.user.roles.append(admin)
|
||||
self.session.commit()
|
||||
self.request.session['is_root'] = True
|
||||
subscribers.new_request_set_user(event, db_session=self.session)
|
||||
self.assertTrue(self.request.has_any_perm('appinfo.list'))
|
||||
|
||||
|
||||
class TestBeforeRender(TestCase):
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ class WebTestCase(DataTestCase):
|
|||
|
||||
def setup_web(self):
|
||||
self.setup_db()
|
||||
self.request = testing.DummyRequest()
|
||||
self.request = self.make_request()
|
||||
self.pyramid_config = testing.setUp(request=self.request, settings={
|
||||
'wutta_config': self.config,
|
||||
'mako.directories': ['wuttaweb:templates'],
|
||||
|
@ -78,6 +78,9 @@ class WebTestCase(DataTestCase):
|
|||
testing.tearDown()
|
||||
self.teardown_db()
|
||||
|
||||
def make_request(self):
|
||||
return testing.DummyRequest()
|
||||
|
||||
|
||||
class NullMenuHandler(MenuHandler):
|
||||
"""
|
||||
|
|
|
@ -331,6 +331,64 @@ class TestMasterView(WebTestCase):
|
|||
# support methods
|
||||
##############################
|
||||
|
||||
def test_has_perm(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting'):
|
||||
view = self.make_view()
|
||||
|
||||
# anonymous user
|
||||
self.assertFalse(view.has_perm('list'))
|
||||
self.assertFalse(self.request.has_perm('list'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
|
||||
# make user with perms
|
||||
barney = model.User(username='barney')
|
||||
self.session.add(barney)
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
barney.roles.append(blokes)
|
||||
auth.grant_permission(blokes, 'settings.list')
|
||||
self.session.commit()
|
||||
|
||||
# this user has perms
|
||||
self.request.user = barney
|
||||
self.assertTrue(view.has_perm('list'))
|
||||
self.assertTrue(self.request.has_perm('settings.list'))
|
||||
|
||||
def test_has_any_perm(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_name='Setting'):
|
||||
view = self.make_view()
|
||||
|
||||
# anonymous user
|
||||
self.assertFalse(view.has_any_perm('list', 'view'))
|
||||
self.assertFalse(self.request.has_any_perm('settings.list', 'settings.view'))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
|
||||
# make user with perms
|
||||
barney = model.User(username='barney')
|
||||
self.session.add(barney)
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
barney.roles.append(blokes)
|
||||
auth.grant_permission(blokes, 'settings.view')
|
||||
self.session.commit()
|
||||
|
||||
# this user has perms
|
||||
self.request.user = barney
|
||||
self.assertTrue(view.has_any_perm('list', 'view'))
|
||||
self.assertTrue(self.request.has_any_perm('settings.list', 'settings.view'))
|
||||
|
||||
def test_render_to_response(self):
|
||||
self.pyramid_config.include('wuttaweb.views.common')
|
||||
self.pyramid_config.include('wuttaweb.views.auth')
|
||||
|
@ -387,6 +445,28 @@ class TestMasterView(WebTestCase):
|
|||
grid = view.make_model_grid(session=self.session)
|
||||
self.assertIs(grid.model_class, model.Setting)
|
||||
|
||||
# no actions by default
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_class=model.Setting):
|
||||
grid = view.make_model_grid(session=self.session)
|
||||
self.assertEqual(grid.actions, [])
|
||||
|
||||
# now let's test some more actions logic
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_class=model.Setting,
|
||||
viewable=True,
|
||||
editable=True,
|
||||
deletable=True):
|
||||
|
||||
# should have 3 actions now, but for lack of perms
|
||||
grid = view.make_model_grid(session=self.session)
|
||||
self.assertEqual(len(grid.actions), 0)
|
||||
|
||||
# but root user has perms, so gets 3 actions
|
||||
with patch.object(self.request, 'is_root', new=True):
|
||||
grid = view.make_model_grid(session=self.session)
|
||||
self.assertEqual(len(grid.actions), 3)
|
||||
|
||||
def test_get_grid_data(self):
|
||||
model = self.app.model
|
||||
self.app.save_setting(self.session, 'foo', 'bar')
|
||||
|
@ -468,6 +548,57 @@ class TestMasterView(WebTestCase):
|
|||
self.request.matchdict = {'name': 'blarg'}
|
||||
self.assertRaises(HTTPNotFound, view.get_instance, session=self.session)
|
||||
|
||||
def test_get_action_url_view(self):
|
||||
model = self.app.model
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
self.session.add(setting)
|
||||
self.session.commit()
|
||||
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_class=model.Setting):
|
||||
master.MasterView.defaults(self.pyramid_config)
|
||||
view = self.make_view()
|
||||
url = view.get_action_url_view(setting, 0)
|
||||
self.assertEqual(url, self.request.route_url('settings.view', name='foo'))
|
||||
|
||||
def test_get_action_url_edit(self):
|
||||
model = self.app.model
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
self.session.add(setting)
|
||||
self.session.commit()
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_class=model.Setting):
|
||||
master.MasterView.defaults(self.pyramid_config)
|
||||
view = self.make_view()
|
||||
|
||||
# typical
|
||||
url = view.get_action_url_edit(setting, 0)
|
||||
self.assertEqual(url, self.request.route_url('settings.edit', name='foo'))
|
||||
|
||||
# but null if instance not editable
|
||||
with patch.object(view, 'is_editable', return_value=False):
|
||||
url = view.get_action_url_edit(setting, 0)
|
||||
self.assertIsNone(url)
|
||||
|
||||
def test_get_action_url_delete(self):
|
||||
model = self.app.model
|
||||
setting = model.Setting(name='foo', value='bar')
|
||||
self.session.add(setting)
|
||||
self.session.commit()
|
||||
with patch.multiple(master.MasterView, create=True,
|
||||
model_class=model.Setting):
|
||||
master.MasterView.defaults(self.pyramid_config)
|
||||
view = self.make_view()
|
||||
|
||||
# typical
|
||||
url = view.get_action_url_delete(setting, 0)
|
||||
self.assertEqual(url, self.request.route_url('settings.delete', name='foo'))
|
||||
|
||||
# but null if instance not deletable
|
||||
with patch.object(view, 'is_deletable', return_value=False):
|
||||
url = view.get_action_url_delete(setting, 0)
|
||||
self.assertIsNone(url)
|
||||
|
||||
def test_make_model_form(self):
|
||||
model = self.app.model
|
||||
|
||||
|
|
|
@ -31,6 +31,42 @@ class TestRoleView(WebTestCase):
|
|||
view.configure_grid(grid)
|
||||
self.assertTrue(grid.is_linked('name'))
|
||||
|
||||
def test_is_editable(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
blokes = model.Role(name="Blokes")
|
||||
self.session.add(blokes)
|
||||
self.session.commit()
|
||||
view = self.make_view()
|
||||
|
||||
admin = auth.get_role_administrator(self.session)
|
||||
authed = auth.get_role_authenticated(self.session)
|
||||
anon = auth.get_role_anonymous(self.session)
|
||||
|
||||
# editable by default
|
||||
self.assertTrue(view.is_editable(blokes))
|
||||
|
||||
# built-in roles not editable by default
|
||||
self.assertFalse(view.is_editable(admin))
|
||||
self.assertFalse(view.is_editable(authed))
|
||||
self.assertFalse(view.is_editable(anon))
|
||||
|
||||
# reset
|
||||
del self.request.user_permissions
|
||||
|
||||
barney = model.User(username='barney')
|
||||
self.session.add(barney)
|
||||
barney.roles.append(blokes)
|
||||
auth.grant_permission(blokes, 'roles.edit_builtin')
|
||||
self.session.commit()
|
||||
|
||||
# user with perms can edit *some* built-in
|
||||
self.request.user = barney
|
||||
self.assertTrue(view.is_editable(authed))
|
||||
self.assertTrue(view.is_editable(anon))
|
||||
# nb. not this one yet
|
||||
self.assertFalse(view.is_editable(admin))
|
||||
|
||||
def test_is_deletable(self):
|
||||
model = self.app.model
|
||||
auth = self.app.get_auth_handler()
|
||||
|
|
Loading…
Reference in a new issue