feat: add support for admin user to become / stop being root
This commit is contained in:
parent
a2ba88ca8f
commit
fc339ba81b
9 changed files with 335 additions and 22 deletions
|
@ -138,9 +138,13 @@ class WuttaSecurityPolicy:
|
|||
return self.session_helper.forget(request, **kw)
|
||||
|
||||
def permits(self, request, context, permission):
|
||||
|
||||
# nb. root user can do anything
|
||||
if getattr(request, 'is_root', False):
|
||||
return True
|
||||
|
||||
config = request.registry.settings['wutta_config']
|
||||
app = config.get_app()
|
||||
auth = app.get_auth_handler()
|
||||
|
||||
user = self.identity(request)
|
||||
return auth.has_permission(self.db_session, user, permission)
|
||||
|
|
|
@ -63,6 +63,16 @@ def new_request(event):
|
|||
|
||||
Reference to the app :term:`config object`.
|
||||
|
||||
.. method:: request.get_referrer(default=None)
|
||||
|
||||
Request method to get the "canonical" HTTP referrer value.
|
||||
This has logic to check for referrer in the request params,
|
||||
user session etc.
|
||||
|
||||
:param default: Optional default URL if none is found in
|
||||
request params/session. If no default is specified,
|
||||
the ``'home'`` route is used.
|
||||
|
||||
.. attribute:: request.use_oruga
|
||||
|
||||
Flag indicating whether the frontend should be displayed using
|
||||
|
@ -75,6 +85,19 @@ def new_request(event):
|
|||
|
||||
request.wutta_config = config
|
||||
|
||||
def get_referrer(default=None):
|
||||
if request.params.get('referrer'):
|
||||
return request.params['referrer']
|
||||
if request.session.get('referrer'):
|
||||
return request.session.pop('referrer')
|
||||
referrer = getattr(request, 'referrer', None)
|
||||
if (not referrer or referrer == request.current_route_url()
|
||||
or not referrer.startswith(request.host_url)):
|
||||
referrer = default or request.route_url('home')
|
||||
return referrer
|
||||
|
||||
request.get_referrer = get_referrer
|
||||
|
||||
def use_oruga(request):
|
||||
spec = config.get('wuttaweb.oruga_detector.spec')
|
||||
if spec:
|
||||
|
@ -104,22 +127,44 @@ def new_request_set_user(event, db_session=None):
|
|||
:class:`~wuttjamaican:wuttjamaican.db.model.auth.User` instance
|
||||
(if logged in), or ``None``.
|
||||
|
||||
:param db_session: Optional :term:`db session` to use, instead of
|
||||
:class:`wuttaweb.db.Session`. Probably only useful for tests.
|
||||
.. attribute:: request.is_admin
|
||||
|
||||
Flag indicating whether current user is a member of the
|
||||
Administrator role.
|
||||
|
||||
.. attribute:: request.is_root
|
||||
|
||||
Flag indicating whether user is currently elevated to root
|
||||
privileges. This is only possible if :attr:`request.is_admin`
|
||||
is also true.
|
||||
"""
|
||||
request = event.request
|
||||
config = request.registry.settings['wutta_config']
|
||||
app = config.get_app()
|
||||
model = app.model
|
||||
|
||||
def user(request):
|
||||
uuid = request.authenticated_userid
|
||||
if uuid:
|
||||
session = db_session or Session()
|
||||
model = app.model
|
||||
return session.get(model.User, uuid)
|
||||
|
||||
request.set_property(user, reify=True)
|
||||
|
||||
def is_admin(request):
|
||||
auth = app.get_auth_handler()
|
||||
return auth.user_is_admin(request.user)
|
||||
|
||||
request.set_property(is_admin, reify=True)
|
||||
|
||||
def is_root(request):
|
||||
if request.is_admin:
|
||||
if request.session.get('is_root', False):
|
||||
return True
|
||||
return False
|
||||
|
||||
request.set_property(is_root, reify=True)
|
||||
|
||||
|
||||
def before_render(event):
|
||||
"""
|
||||
|
|
|
@ -314,8 +314,29 @@
|
|||
<%def name="render_user_menu()">
|
||||
% if request.user:
|
||||
<div class="navbar-item has-dropdown is-hoverable">
|
||||
<a class="navbar-link">${request.user}</a>
|
||||
<a class="navbar-link ${'has-background-danger has-text-white' if request.is_root else ''}">${request.user}</a>
|
||||
<div class="navbar-dropdown">
|
||||
% if request.is_root:
|
||||
${h.form(url('stop_root'), ref='stopBeingRootForm')}
|
||||
## TODO
|
||||
## ${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="stopBeingRoot()"
|
||||
class="navbar-item has-background-danger has-text-white">
|
||||
Stop being root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
% elif request.is_admin:
|
||||
${h.form(url('become_root'), ref='startBeingRootForm')}
|
||||
## TODO
|
||||
## ${h.csrf_token(request)}
|
||||
<input type="hidden" name="referrer" value="${request.current_route_url()}" />
|
||||
<a @click="startBeingRoot()"
|
||||
class="navbar-item has-background-danger has-text-white">
|
||||
Become root
|
||||
</a>
|
||||
${h.end_form()}
|
||||
% endif
|
||||
${h.link_to("Change Password", url('change_password'), class_='navbar-item')}
|
||||
${h.link_to("Logout", url('logout'), class_='navbar-item')}
|
||||
</div>
|
||||
|
@ -359,6 +380,18 @@
|
|||
const key = 'menu_' + hash + '_shown'
|
||||
this[key] = !this[key]
|
||||
},
|
||||
|
||||
% if request.is_admin:
|
||||
|
||||
startBeingRoot() {
|
||||
this.$refs.startBeingRootForm.submit()
|
||||
},
|
||||
|
||||
stopBeingRoot() {
|
||||
this.$refs.stopBeingRootForm.submit()
|
||||
},
|
||||
|
||||
% endif
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -198,6 +198,48 @@ class AuthView(View):
|
|||
if auth.check_user_password(user, value):
|
||||
node.raise_invalid("New password must be different from old password.")
|
||||
|
||||
def become_root(self):
|
||||
"""
|
||||
Elevate the current request to 'root' for full system access.
|
||||
|
||||
This is only allowed if current (authenticated) user is a
|
||||
member of the Administrator role. Also note that GET is not
|
||||
allowed for this view, only POST.
|
||||
|
||||
See also :meth:`stop_root()`.
|
||||
"""
|
||||
if self.request.method != 'POST':
|
||||
raise self.forbidden()
|
||||
|
||||
if not self.request.is_admin:
|
||||
raise self.forbidden()
|
||||
|
||||
self.request.session['is_root'] = True
|
||||
self.request.session.flash("You have been elevated to 'root' and now have full system access")
|
||||
|
||||
url = self.request.get_referrer()
|
||||
return self.redirect(url)
|
||||
|
||||
def stop_root(self):
|
||||
"""
|
||||
Lower the current request from 'root' back to normal access.
|
||||
|
||||
Also note that GET is not allowed for this view, only POST.
|
||||
|
||||
See also :meth:`become_root()`.
|
||||
"""
|
||||
if self.request.method != 'POST':
|
||||
raise self.forbidden()
|
||||
|
||||
if not self.request.is_admin:
|
||||
raise self.forbidden()
|
||||
|
||||
self.request.session['is_root'] = False
|
||||
self.request.session.flash("Your normal system access has been restored")
|
||||
|
||||
url = self.request.get_referrer()
|
||||
return self.redirect(url)
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._auth_defaults(config)
|
||||
|
@ -222,6 +264,18 @@ class AuthView(View):
|
|||
route_name='change_password',
|
||||
renderer='/auth/change_password.mako')
|
||||
|
||||
# become root
|
||||
config.add_route('become_root', '/root/yes',
|
||||
request_method='POST')
|
||||
config.add_view(cls, attr='become_root',
|
||||
route_name='become_root')
|
||||
|
||||
# stop root
|
||||
config.add_route('stop_root', '/root/no',
|
||||
request_method='POST')
|
||||
config.add_view(cls, attr='stop_root',
|
||||
route_name='stop_root')
|
||||
|
||||
|
||||
def defaults(config, **kwargs):
|
||||
base = globals()
|
||||
|
|
|
@ -55,6 +55,14 @@ class View:
|
|||
self.config = self.request.wutta_config
|
||||
self.app = self.config.get_app()
|
||||
|
||||
def forbidden(self):
|
||||
"""
|
||||
Convenience method, to raise a HTTP 403 Forbidden exception::
|
||||
|
||||
raise self.forbidden()
|
||||
"""
|
||||
return httpexceptions.HTTPForbidden()
|
||||
|
||||
def make_form(self, **kwargs):
|
||||
"""
|
||||
Make and return a new :class:`~wuttaweb.forms.base.Form`
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue