tailbone/tailbone/subscribers.py
Lance Edgar 79e71ec4ab Cache user permissions upon "new request" event
this avoids a ton of (often redundant) SQL queries when checking permissions
2018-10-17 12:50:02 -05:00

180 lines
6 KiB
Python

# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Event Subscribers
"""
from __future__ import unicode_literals, absolute_import
import six
import json
import datetime
import rattail
from rattail.db import model
from rattail.db.auth import cache_permissions
from pyramid import threadlocal
import tailbone
from tailbone import helpers
from tailbone.db import Session
def new_request(event):
"""
Identify the current user, and cache their current permissions. Also adds
the ``rattail_config`` attribute to the request.
A global Rattail ``config`` should already be present within the Pyramid
application registry's settings, which would normally be accessed via::
request.registry.settings['rattail_config']
This function merely "promotes" that config object so that it is more
directly accessible, a la::
request.rattail_config
.. note::
This of course assumes that a Rattail ``config`` object *has* in fact
already been placed in the application registry settings. If this is
not the case, this function will do nothing.
"""
request = event.request
rattail_config = request.registry.settings.get('rattail_config')
if rattail_config:
request.rattail_config = rattail_config
request.user = None
uuid = request.authenticated_userid
if uuid:
request.user = Session.query(model.User).get(uuid)
if request.user:
Session().set_continuum_user(request.user)
request.is_admin = bool(request.user) and request.user.is_admin()
request.is_root = request.is_admin and request.session.get('is_root', False)
request.tailbone_cached_permissions = cache_permissions(Session(), request.user)
def before_render(event):
"""
Adds goodies to the global template renderer context.
"""
request = event.get('request') or threadlocal.get_current_request()
renderer_globals = event
renderer_globals['h'] = helpers
renderer_globals['url'] = request.route_url
renderer_globals['rattail'] = rattail
renderer_globals['tailbone'] = tailbone
renderer_globals['enum'] = request.rattail_config.get_enum()
renderer_globals['six'] = six
renderer_globals['json'] = json
renderer_globals['datetime'] = datetime
def add_inbox_count(event):
"""
Adds the current user's inbox message count to the global renderer context.
Note that this is not enabled by default; to turn it on you must do this:
config.add_subscriber('tailbone.subscribers.add_inbox_count', 'pyramid.events.BeforeRender')
"""
request = event.get('request') or threadlocal.get_current_request()
if request.user:
renderer_globals = event
enum = request.rattail_config.get_enum()
renderer_globals['inbox_count'] = Session.query(model.Message)\
.outerjoin(model.MessageRecipient)\
.filter(model.MessageRecipient.recipient == Session.merge(request.user))\
.filter(model.MessageRecipient.status == enum.MESSAGE_STATUS_INBOX)\
.count()
def context_found(event):
"""
Attach some goodies to the request object.
The following is attached to the request:
* 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 fetching the referrer, as ``get_referrer()``.
"""
request = event.request
def has_perm(name):
if name in request.tailbone_cached_permissions:
return True
return request.is_root
request.has_perm = has_perm
def has_any_perm(*names):
for name in names:
if has_perm(name):
return True
return False
request.has_any_perm = has_any_perm
def get_referrer(default=None, mobile=False):
if request.params.get('referrer'):
return request.params['referrer']
if request.session.get('referrer'):
return request.session.pop('referrer')
referrer = request.referrer
if (not referrer or referrer == request.current_route_url()
or not referrer.startswith(request.host_url)):
if default:
referrer = default
elif mobile:
referrer = request.route_url('mobile.home')
else:
referrer = request.route_url('home')
return referrer
request.get_referrer = get_referrer
def get_session_timeout():
"""
Returns the timeout in effect for the current session
"""
return request.session.get('_timeout')
request.get_session_timeout = get_session_timeout
def includeme(config):
config.add_subscriber(new_request, 'pyramid.events.NewRequest')
config.add_subscriber(before_render, 'pyramid.events.BeforeRender')
config.add_subscriber(context_found, 'pyramid.events.ContextFound')