From 75c73aad1392c1b0397c06f83e6ff482992042bf Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 21 Feb 2017 13:12:23 -0600 Subject: [PATCH] Expose/honor per-role session timeouts --- tailbone/auth.py | 36 +++++++++----------------------- tailbone/views/roles.py | 46 ++++++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/tailbone/auth.py b/tailbone/auth.py index ae64ff17..6ab26209 100644 --- a/tailbone/auth.py +++ b/tailbone/auth.py @@ -29,7 +29,7 @@ from __future__ import unicode_literals, absolute_import import logging from rattail.db import model -from rattail.db.auth import has_permission +from rattail.db.auth import has_permission, authenticated_role from rattail.util import prettify, NOTSET from zope.interface import implementer @@ -42,44 +42,28 @@ from tailbone.db import Session log = logging.getLogger(__name__) -def login_user(request, user, type_='default', timeout=NOTSET): +def login_user(request, user, timeout=NOTSET): """ Perform the steps necessary to login the given user. Note that this returns a ``headers`` dict which you should pass to the redirect. """ headers = remember(request, user.uuid) if timeout is NOTSET: - timeout = get_session_timeout_for_user(request.rattail_config, user, type_) or None + timeout = session_timeout_for_user(user) log.debug("setting session timeout for '{}' to {}".format(user.username, timeout)) set_session_timeout(request, timeout) return headers -def get_session_timeout_for_user(config, user, type_='default'): +def session_timeout_for_user(user): """ - Must return a value to be used to set the session timeout for the given - user. By default this will return ``None`` if the user has the - "forever session" permission, otherwise will try to read a default - value from config: - - .. code-block:: ini - - [tailbone] - - # set session timeout to 10 minutes: - session.timeout.default = 600 - - # or, set to 0 to disable: - #session.timeout.default = 0 + Returns the "max" session timeout for the user, according to roles """ - if not has_permission(Session(), user, 'general.forever_session'): - timeout = config.getint('tailbone', 'session.timeout.{}'.format(type_)) - - # TODO: remove this hack after no longer needed - if timeout is None and type_ == 'default': - timeout = config.getint('tailbone', 'session.default_timeout') - - return timeout if timeout is not None else 300 # 5 minutes + roles = user.roles + [authenticated_role(Session())] + timeouts = [role.session_timeout for role in roles + if role.session_timeout is not None] + if timeouts and 0 not in timeouts: + return max(timeouts) def set_session_timeout(request, timeout): diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py index fef86cad..82fe6efe 100644 --- a/tailbone/views/roles.py +++ b/tailbone/views/roles.py @@ -31,7 +31,8 @@ from sqlalchemy import orm from rattail.db import model from rattail.db.auth import has_permission, administrator_role, guest_role, authenticated_role -import formalchemy +import formalchemy as fa +from formalchemy.fields import IntegerFieldRenderer from webhelpers.html import HTML, tags from tailbone import forms @@ -41,40 +42,38 @@ from tailbone.views.continuum import VersionView, version_defaults from tailbone.newgrids import AlchemyGrid, GridAction -class PermissionsField(formalchemy.Field): - """ - Custom field for role permissions. - """ - - def sync(self): - if not self.is_readonly(): - role = self.model - role.permissions = self.renderer.deserialize() - - class RolesView(PrincipalMasterView): """ Master view for the Role model. """ model_class = model.Role - def configure_grid(self, g): + def _preconfigure_grid(self, g): g.filters['name'].default_active = True g.filters['name'].default_verb = 'contains' g.default_sortkey = 'name' + + def configure_grid(self, g): g.configure( include=[ g.name, + g.session_timeout, ], readonly=True) - def configure_fieldset(self, fs): + def _preconfigure_fieldset(self, fs): fs.append(PermissionsField('permissions')) permissions = self.request.registry.settings.get('tailbone_permissions', {}) fs.permissions.set(renderer=forms.renderers.PermissionsFieldRenderer(permissions)) + fs.session_timeout.set(renderer=SessionTimeoutRenderer) + if (self.viewing or self.editing) and fs.model is guest_role(self.Session()): + fs.session_timeout.set(readonly=True, attrs={'applicable': False}) + + def configure_fieldset(self, fs): fs.configure( include=[ fs.name, + fs.session_timeout, fs.permissions, ]) @@ -122,6 +121,25 @@ class RolesView(PrincipalMasterView): return roles +class SessionTimeoutRenderer(IntegerFieldRenderer): + + def render_readonly(self, **kwargs): + if not kwargs.pop('applicable', True): + return "(not applicable)" + return super(SessionTimeoutRenderer, self).render_readonly(**kwargs) + + +class PermissionsField(fa.Field): + """ + Custom field for role permissions. + """ + + def sync(self): + if not self.is_readonly(): + role = self.model + role.permissions = self.renderer.deserialize() + + class RoleVersionView(VersionView): """ View which shows version history for a role.