Let any 'admin' user elevate to 'root' for full system access
But otherwise, let the Administrator role be "normal" and have perms of its own. Hopefully cuts down on unwanted screen noise for admins.
This commit is contained in:
parent
4599eaad97
commit
6bf60365ba
|
@ -106,11 +106,22 @@ def provide_postgresql_settings(settings):
|
||||||
settings.setdefault('tm.attempts', 2)
|
settings.setdefault('tm.attempts', 2)
|
||||||
|
|
||||||
|
|
||||||
|
class Root(dict):
|
||||||
|
"""
|
||||||
|
Root factory for Pyramid. This is necessary to make the current request
|
||||||
|
available to the authorization policy object, which needs it to check if
|
||||||
|
the current request "is root".
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
|
||||||
def make_pyramid_config(settings):
|
def make_pyramid_config(settings):
|
||||||
"""
|
"""
|
||||||
Make a Pyramid config object from the given settings.
|
Make a Pyramid config object from the given settings.
|
||||||
"""
|
"""
|
||||||
config = Configurator(settings=settings)
|
config = Configurator(settings=settings, root_factory=Root)
|
||||||
|
|
||||||
# Configure user authentication / authorization.
|
# Configure user authentication / authorization.
|
||||||
config.set_authentication_policy(SessionAuthenticationPolicy())
|
config.set_authentication_policy(SessionAuthenticationPolicy())
|
||||||
|
|
|
@ -43,9 +43,14 @@ class TailboneAuthorizationPolicy(object):
|
||||||
def permits(self, context, principals, permission):
|
def permits(self, context, principals, permission):
|
||||||
for userid in principals:
|
for userid in principals:
|
||||||
if userid not in (Everyone, Authenticated):
|
if userid not in (Everyone, Authenticated):
|
||||||
user = Session.query(model.User).get(userid)
|
if context.request.user and context.request.user.uuid == userid:
|
||||||
if user:
|
return context.request.has_perm(permission)
|
||||||
return has_permission(Session(), user, permission)
|
else:
|
||||||
|
assert False # should no longer happen..right?
|
||||||
|
user = Session.query(model.User).get(userid)
|
||||||
|
if user:
|
||||||
|
if has_permission(Session(), user, permission):
|
||||||
|
return True
|
||||||
if Everyone in principals:
|
if Everyone in principals:
|
||||||
return has_permission(Session(), None, permission)
|
return has_permission(Session(), None, permission)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -61,35 +61,27 @@ def PermissionsFieldRenderer(permissions, include_guest=False, include_authentic
|
||||||
|
|
||||||
def _render(self, readonly=False, **kwargs):
|
def _render(self, readonly=False, **kwargs):
|
||||||
principal = self.field.model
|
principal = self.field.model
|
||||||
if isinstance(principal, model.Role) and principal is administrator_role(Session()):
|
html = ''
|
||||||
html = HTML.tag('p', c="This is the administrative role; "
|
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
|
||||||
"it has full access to the entire system.")
|
inner = HTML.tag('p', c=permissions[groupkey]['label'])
|
||||||
if not readonly:
|
perms = permissions[groupkey]['perms']
|
||||||
html += tags.hidden(self.name, value='') # ugly hack..or good idea?
|
rendered = False
|
||||||
else:
|
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
|
||||||
html = ''
|
checked = has_permission(Session(), principal, key,
|
||||||
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
|
include_guest=include_guest,
|
||||||
inner = HTML.tag('p', c=permissions[groupkey]['label'])
|
include_authenticated=include_authenticated)
|
||||||
perms = permissions[groupkey]['perms']
|
if checked or not readonly:
|
||||||
rendered = False
|
label = perms[key]['label']
|
||||||
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
|
if readonly:
|
||||||
checked = has_permission(Session(), principal, key,
|
span = HTML.tag('span', c="[X]" if checked else "[ ]")
|
||||||
include_guest=include_guest,
|
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
|
||||||
include_authenticated=include_authenticated)
|
else:
|
||||||
if checked or not readonly:
|
inner += tags.checkbox(self.name + '-' + key,
|
||||||
label = perms[key]['label']
|
checked=checked, label=label)
|
||||||
if readonly:
|
rendered = True
|
||||||
span = HTML.tag('span', c="[X]" if checked else "[ ]")
|
if rendered:
|
||||||
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
|
html += HTML.tag('div', class_='group', c=inner)
|
||||||
else:
|
return html or "(none granted)"
|
||||||
inner += tags.checkbox(self.name + '-' + key,
|
|
||||||
checked=checked, label=label)
|
|
||||||
rendered = True
|
|
||||||
if rendered:
|
|
||||||
html += HTML.tag('div', class_='group', c=inner)
|
|
||||||
if not html:
|
|
||||||
return "(none granted)"
|
|
||||||
return html
|
|
||||||
|
|
||||||
def render(self, **kwargs):
|
def render(self, **kwargs):
|
||||||
return self._render(**kwargs)
|
return self._render(**kwargs)
|
||||||
|
|
|
@ -99,3 +99,18 @@ ul.error li {
|
||||||
ul.ui-menu {
|
ul.ui-menu {
|
||||||
max-height: 30em;
|
max-height: 30em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************
|
||||||
|
* tweaks for root user
|
||||||
|
******************************/
|
||||||
|
|
||||||
|
.menubar .root-user .ui-button-text,
|
||||||
|
.menubar .root-user.ui-menu-item a {
|
||||||
|
background-color: red;
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menubar .root-user.ui-menu-item a {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ from __future__ import unicode_literals, absolute_import
|
||||||
import rattail
|
import rattail
|
||||||
from rattail import enum
|
from rattail import enum
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.db.auth import has_permission
|
from rattail.db.auth import has_permission, administrator_role
|
||||||
|
|
||||||
from pyramid import threadlocal
|
from pyramid import threadlocal
|
||||||
from pyramid.security import authenticated_userid
|
from pyramid.security import authenticated_userid
|
||||||
|
@ -108,6 +108,10 @@ def context_found(event):
|
||||||
|
|
||||||
* The currently logged-in user instance (if any), as ``user``.
|
* The currently logged-in user instance (if any), as ``user``.
|
||||||
|
|
||||||
|
* ``is_admin`` flag indicating whether user has the Administrator role.
|
||||||
|
|
||||||
|
* ``is_root`` flag indicating whether user is currently elevated to root.
|
||||||
|
|
||||||
* A shortcut method for permission checking, as ``has_perm()``.
|
* A shortcut method for permission checking, as ``has_perm()``.
|
||||||
|
|
||||||
* A shortcut method for fetching the referrer, as ``get_referrer()``.
|
* A shortcut method for fetching the referrer, as ``get_referrer()``.
|
||||||
|
@ -122,13 +126,18 @@ def context_found(event):
|
||||||
if request.user:
|
if request.user:
|
||||||
Session().set_continuum_user(request.user)
|
Session().set_continuum_user(request.user)
|
||||||
|
|
||||||
def has_perm(perm):
|
request.is_admin = request.user and administrator_role(Session()) in request.user.roles
|
||||||
return has_permission(Session(), request.user, perm)
|
request.is_root = request.is_admin and request.session.get('is_root', False)
|
||||||
|
|
||||||
|
def has_perm(name):
|
||||||
|
if has_permission(Session(), request.user, name):
|
||||||
|
return True
|
||||||
|
return request.is_root
|
||||||
request.has_perm = has_perm
|
request.has_perm = has_perm
|
||||||
|
|
||||||
def has_any_perm(*perms):
|
def has_any_perm(*names):
|
||||||
for perm in perms:
|
for name in names:
|
||||||
if has_permission(Session(), request.user, perm):
|
if has_perm(name):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
request.has_any_perm = has_any_perm
|
request.has_any_perm = has_any_perm
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2015 Lance Edgar
|
# Copyright © 2010-2016 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -24,20 +24,19 @@
|
||||||
Auth Views
|
Auth Views
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from rattail.db.auth import authenticate_user, set_user_password
|
||||||
|
|
||||||
|
import formencode
|
||||||
from pyramid.httpexceptions import HTTPFound
|
from pyramid.httpexceptions import HTTPFound
|
||||||
from pyramid.security import remember, forget, authenticated_userid
|
from pyramid.security import remember, forget, authenticated_userid
|
||||||
|
from pyramid_simpleform import Form
|
||||||
from webhelpers.html import literal
|
from webhelpers.html import literal
|
||||||
from webhelpers.html import tags
|
from webhelpers.html import tags
|
||||||
|
|
||||||
import formencode
|
from tailbone.db import Session
|
||||||
from pyramid_simpleform import Form
|
from tailbone.forms.simpleform import FormRenderer
|
||||||
from ..forms.simpleform import FormRenderer
|
|
||||||
|
|
||||||
from ..db import Session
|
|
||||||
from rattail.db.auth import authenticate_user, set_user_password
|
|
||||||
|
|
||||||
|
|
||||||
def forbidden(request):
|
def forbidden(request):
|
||||||
|
@ -104,6 +103,24 @@ def logout(request):
|
||||||
return HTTPFound(location=referrer, headers=headers)
|
return HTTPFound(location=referrer, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
|
def become_root(request):
|
||||||
|
"""
|
||||||
|
Elevate the current request to 'root' for full system access.
|
||||||
|
"""
|
||||||
|
request.session['is_root'] = True
|
||||||
|
request.session.flash("You have been elevated to 'root' and now have full system access", 'error')
|
||||||
|
return HTTPFound(location=request.get_referrer())
|
||||||
|
|
||||||
|
|
||||||
|
def stop_root(request):
|
||||||
|
"""
|
||||||
|
Lower the current request from 'root' back to normal access.
|
||||||
|
"""
|
||||||
|
request.session['is_root'] = False
|
||||||
|
request.session.flash("Your normal system access has been restored")
|
||||||
|
return HTTPFound(location=request.get_referrer())
|
||||||
|
|
||||||
|
|
||||||
class CurrentPasswordCorrect(formencode.validators.FancyValidator):
|
class CurrentPasswordCorrect(formencode.validators.FancyValidator):
|
||||||
|
|
||||||
def _to_python(self, value, state):
|
def _to_python(self, value, state):
|
||||||
|
@ -148,6 +165,8 @@ def change_password(request):
|
||||||
def add_routes(config):
|
def add_routes(config):
|
||||||
config.add_route('login', '/login')
|
config.add_route('login', '/login')
|
||||||
config.add_route('logout', '/logout')
|
config.add_route('logout', '/logout')
|
||||||
|
config.add_route('become_root', '/root/yes')
|
||||||
|
config.add_route('stop_root', '/root/no')
|
||||||
config.add_route('change_password', '/change-password')
|
config.add_route('change_password', '/change-password')
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,5 +180,8 @@ def includeme(config):
|
||||||
|
|
||||||
config.add_view(logout, route_name='logout')
|
config.add_view(logout, route_name='logout')
|
||||||
|
|
||||||
|
config.add_view(become_root, route_name='become_root')
|
||||||
|
config.add_view(stop_root, route_name='stop_root')
|
||||||
|
|
||||||
config.add_view(change_password, route_name='change_password',
|
config.add_view(change_password, route_name='change_password',
|
||||||
renderer='/change_password.mako')
|
renderer='/change_password.mako')
|
||||||
|
|
Loading…
Reference in a new issue