Overhaul how available permissions are registered in app config.
Permissions must now be regsistered just like routes and views. This should make things much nicer going forward.
This commit is contained in:
parent
412ac6e12f
commit
9cfbc918e7
|
@ -117,6 +117,18 @@ def make_pyramid_config(settings):
|
||||||
config.include('pyramid_mako')
|
config.include('pyramid_mako')
|
||||||
config.include('pyramid_tm')
|
config.include('pyramid_tm')
|
||||||
|
|
||||||
|
# Add some permissions magic.
|
||||||
|
config.add_directive('add_tailbone_permission_group', 'tailbone.auth.add_permission_group')
|
||||||
|
config.add_directive('add_tailbone_permission', 'tailbone.auth.add_permission')
|
||||||
|
|
||||||
|
# TODO: This can finally be removed once all CRUD/index views have been
|
||||||
|
# converted to use the new master view etc.
|
||||||
|
for label, perms in settings.get('edbob.permissions'):
|
||||||
|
groupkey = label.lower().replace(' ', '_')
|
||||||
|
config.add_tailbone_permission_group(groupkey, label)
|
||||||
|
for key, label in perms:
|
||||||
|
config.add_tailbone_permission(groupkey, key, label)
|
||||||
|
|
||||||
# Configure FormAlchemy.
|
# Configure FormAlchemy.
|
||||||
formalchemy.config.engine = TemplateEngine()
|
formalchemy.config.engine = TemplateEngine()
|
||||||
formalchemy.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
|
formalchemy.FieldSet.default_renderers[GPCType] = renderers.GPCFieldRenderer
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2012 Lance Edgar
|
# Copyright © 2010-2015 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -21,28 +20,31 @@
|
||||||
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Authentication & Authorization
|
Authentication & Authorization
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from edbob.util import prettify
|
||||||
|
|
||||||
|
from rattail.db import model
|
||||||
|
from rattail.db.auth import has_permission
|
||||||
|
|
||||||
from zope.interface import implementer
|
from zope.interface import implementer
|
||||||
from pyramid.interfaces import IAuthorizationPolicy
|
from pyramid.interfaces import IAuthorizationPolicy
|
||||||
from pyramid.security import Everyone, Authenticated
|
from pyramid.security import Everyone, Authenticated
|
||||||
|
|
||||||
from .db import Session
|
from tailbone.db import Session
|
||||||
|
|
||||||
|
|
||||||
@implementer(IAuthorizationPolicy)
|
@implementer(IAuthorizationPolicy)
|
||||||
class TailboneAuthorizationPolicy(object):
|
class TailboneAuthorizationPolicy(object):
|
||||||
|
|
||||||
def permits(self, context, principals, permission):
|
def permits(self, context, principals, permission):
|
||||||
from rattail.db.model import User
|
|
||||||
from rattail.db.auth import has_permission
|
|
||||||
|
|
||||||
for userid in principals:
|
for userid in principals:
|
||||||
if userid not in (Everyone, Authenticated):
|
if userid not in (Everyone, Authenticated):
|
||||||
user = Session.query(User).get(userid)
|
user = Session.query(model.User).get(userid)
|
||||||
if user:
|
if user:
|
||||||
return has_permission(Session(), user, permission)
|
return has_permission(Session(), user, permission)
|
||||||
if Everyone in principals:
|
if Everyone in principals:
|
||||||
|
@ -51,3 +53,29 @@ class TailboneAuthorizationPolicy(object):
|
||||||
|
|
||||||
def principals_allowed_by_permission(self, context, permission):
|
def principals_allowed_by_permission(self, context, permission):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def add_permission_group(config, key, label=None):
|
||||||
|
"""
|
||||||
|
Add a permission group to the app configuration.
|
||||||
|
"""
|
||||||
|
def action():
|
||||||
|
perms = config.get_settings().get('tailbone_permissions', {})
|
||||||
|
group = perms.setdefault(key, {'key': key})
|
||||||
|
group['label'] = label or prettify(key)
|
||||||
|
config.add_settings({'tailbone_permissions': perms})
|
||||||
|
config.action(None, action)
|
||||||
|
|
||||||
|
|
||||||
|
def add_permission(config, groupkey, key, label=None):
|
||||||
|
"""
|
||||||
|
Add a permission to the app configuration.
|
||||||
|
"""
|
||||||
|
def action():
|
||||||
|
perms = config.get_settings().get('tailbone_permissions', {})
|
||||||
|
group = perms.setdefault(groupkey, {'key': groupkey})
|
||||||
|
group.setdefault('label', prettify(groupkey))
|
||||||
|
perm = group.setdefault('perms', {}).setdefault(key, {'key': key})
|
||||||
|
perm['label'] = label or prettify(key)
|
||||||
|
config.add_settings({'tailbone_permissions': perms})
|
||||||
|
config.action(None, action)
|
||||||
|
|
|
@ -441,28 +441,42 @@ class MasterView(View):
|
||||||
url_prefix = cls.get_url_prefix()
|
url_prefix = cls.get_url_prefix()
|
||||||
permission_prefix = cls.get_permission_prefix()
|
permission_prefix = cls.get_permission_prefix()
|
||||||
model_key = cls.get_model_key()
|
model_key = cls.get_model_key()
|
||||||
|
model_title = cls.get_model_title()
|
||||||
|
model_title_plural = cls.get_model_title_plural()
|
||||||
|
|
||||||
|
config.add_tailbone_permission_group(permission_prefix, model_title_plural)
|
||||||
|
|
||||||
# list/search
|
# list/search
|
||||||
config.add_route(route_prefix, '{0}/'.format(url_prefix))
|
config.add_route(route_prefix, '{0}/'.format(url_prefix))
|
||||||
config.add_view(cls, attr='index', route_name=route_prefix,
|
config.add_view(cls, attr='index', route_name=route_prefix,
|
||||||
permission='{0}.list'.format(permission_prefix))
|
permission='{0}.list'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{0}.list'.format(permission_prefix),
|
||||||
|
"List/Search {0}".format(model_title_plural))
|
||||||
|
|
||||||
# create
|
# create
|
||||||
config.add_route('{0}.create'.format(route_prefix), '{0}/new'.format(url_prefix))
|
config.add_route('{0}.create'.format(route_prefix), '{0}/new'.format(url_prefix))
|
||||||
config.add_view(cls, attr='create', route_name='{0}.create'.format(route_prefix),
|
config.add_view(cls, attr='create', route_name='{0}.create'.format(route_prefix),
|
||||||
permission='{0}.create'.format(permission_prefix))
|
permission='{0}.create'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{0}.create'.format(permission_prefix),
|
||||||
|
"Create new {0}".format(model_title_plural))
|
||||||
|
|
||||||
# view
|
# view
|
||||||
config.add_route('{0}.view'.format(route_prefix), '{0}/{{{1}}}'.format(url_prefix, model_key))
|
config.add_route('{0}.view'.format(route_prefix), '{0}/{{{1}}}'.format(url_prefix, model_key))
|
||||||
config.add_view(cls, attr='view', route_name='{0}.view'.format(route_prefix),
|
config.add_view(cls, attr='view', route_name='{0}.view'.format(route_prefix),
|
||||||
permission='{0}.view'.format(permission_prefix))
|
permission='{0}.view'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{0}.view'.format(permission_prefix),
|
||||||
|
"View {0} Details".format(model_title))
|
||||||
|
|
||||||
# edit
|
# edit
|
||||||
config.add_route('{0}.edit'.format(route_prefix), '{0}/{{{1}}}/edit'.format(url_prefix, model_key))
|
config.add_route('{0}.edit'.format(route_prefix), '{0}/{{{1}}}/edit'.format(url_prefix, model_key))
|
||||||
config.add_view(cls, attr='edit', route_name='{0}.edit'.format(route_prefix),
|
config.add_view(cls, attr='edit', route_name='{0}.edit'.format(route_prefix),
|
||||||
permission='{0}.edit'.format(permission_prefix))
|
permission='{0}.edit'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{0}.edit'.format(permission_prefix),
|
||||||
|
"Edit {0}".format(model_title_plural))
|
||||||
|
|
||||||
# delete
|
# delete
|
||||||
config.add_route('{0}.delete'.format(route_prefix), '{0}/{{{1}}}/delete'.format(url_prefix, model_key))
|
config.add_route('{0}.delete'.format(route_prefix), '{0}/{{{1}}}/delete'.format(url_prefix, model_key))
|
||||||
config.add_view(cls, attr='delete', route_name='{0}.delete'.format(route_prefix),
|
config.add_view(cls, attr='delete', route_name='{0}.delete'.format(route_prefix),
|
||||||
permission='{0}.delete'.format(permission_prefix))
|
permission='{0}.delete'.format(permission_prefix))
|
||||||
|
config.add_tailbone_permission(permission_prefix, '{0}.delete'.format(permission_prefix),
|
||||||
|
"Delete {0}".format(model_title_plural))
|
||||||
|
|
|
@ -49,14 +49,10 @@ class PermissionsField(formalchemy.Field):
|
||||||
role.permissions = self.renderer.deserialize()
|
role.permissions = self.renderer.deserialize()
|
||||||
|
|
||||||
|
|
||||||
def OldPermissionsFieldRenderer(permissions, *args, **kwargs):
|
def PermissionsFieldRenderer(permissions, *args, **kwargs):
|
||||||
|
|
||||||
perms = permissions
|
|
||||||
|
|
||||||
class PermissionsFieldRenderer(formalchemy.FieldRenderer):
|
class PermissionsFieldRenderer(formalchemy.FieldRenderer):
|
||||||
|
|
||||||
permissions = perms
|
|
||||||
|
|
||||||
def deserialize(self):
|
def deserialize(self):
|
||||||
perms = []
|
perms = []
|
||||||
i = len(self.name) + 1
|
i = len(self.name) + 1
|
||||||
|
@ -75,17 +71,18 @@ def OldPermissionsFieldRenderer(permissions, *args, **kwargs):
|
||||||
html += tags.hidden(self.name, value='') # ugly hack..or good idea?
|
html += tags.hidden(self.name, value='') # ugly hack..or good idea?
|
||||||
else:
|
else:
|
||||||
html = ''
|
html = ''
|
||||||
for group, perms in self.permissions:
|
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label']):
|
||||||
inner = HTML.tag('p', c=group)
|
inner = HTML.tag('p', c=permissions[groupkey]['label'])
|
||||||
for perm, title in perms:
|
perms = permissions[groupkey]['perms']
|
||||||
checked = has_permission(
|
for key in sorted(perms, key=lambda p: perms[p]['label']):
|
||||||
Session(), role, perm, include_guest=False)
|
checked = has_permission(Session(), role, key, include_guest=False)
|
||||||
|
label = perms[key]['label']
|
||||||
if readonly:
|
if readonly:
|
||||||
span = HTML.tag('span', c="[X]" if checked else "[ ]")
|
span = HTML.tag('span', c="[X]" if checked else "[ ]")
|
||||||
inner += HTML.tag('p', class_='perm', c=span + ' ' + title)
|
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
|
||||||
else:
|
else:
|
||||||
inner += tags.checkbox(self.name + '-' + perm,
|
inner += tags.checkbox(self.name + '-' + key,
|
||||||
checked=checked, label=title)
|
checked=checked, label=label)
|
||||||
html += HTML.tag('div', class_='group', c=inner)
|
html += HTML.tag('div', class_='group', c=inner)
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
@ -116,7 +113,8 @@ class RolesView(MasterView):
|
||||||
|
|
||||||
def configure_fieldset(self, fs):
|
def configure_fieldset(self, fs):
|
||||||
fs.append(PermissionsField('permissions'))
|
fs.append(PermissionsField('permissions'))
|
||||||
fs.permissions.set(renderer=OldPermissionsFieldRenderer(self.old_permissions))
|
permissions = self.request.registry.settings.get('tailbone_permissions', {})
|
||||||
|
fs.permissions.set(renderer=PermissionsFieldRenderer(permissions))
|
||||||
fs.configure(
|
fs.configure(
|
||||||
include=[
|
include=[
|
||||||
fs.name,
|
fs.name,
|
||||||
|
@ -159,15 +157,9 @@ class RoleVersionView(VersionView):
|
||||||
View which shows version history for a role.
|
View which shows version history for a role.
|
||||||
"""
|
"""
|
||||||
parent_class = model.Role
|
parent_class = model.Role
|
||||||
route_model_view = 'role.read'
|
route_model_view = 'roles.view'
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
|
|
||||||
# TODO: This can finally be removed once all CRUD/index views have been
|
|
||||||
# converted to use the new master view etc.
|
|
||||||
settings = config.get_settings()
|
|
||||||
RolesView.old_permissions = settings.get('edbob.permissions')
|
|
||||||
|
|
||||||
RolesView.defaults(config)
|
RolesView.defaults(config)
|
||||||
version_defaults(config, RoleVersionView, 'role')
|
version_defaults(config, RoleVersionView, 'role')
|
||||||
|
|
Loading…
Reference in a new issue