Add a session cache of can_access call results

This patch adds a decorator and function to all horizon components so
that the results of calls to can_access are cached in the session. This
should prevent large numbers of calls to APIs when can_access functions
need to get information from services. The cache is stored in
session['allowed'] using self.__class__ as the key, this ensures each
key is human readable and can be easily mocked out in future tests.

Change-Id: Ic65e4cb6499ecae4b09dd525416656a6f41853f5
Closes-Bug: 1367716
This commit is contained in:
Sam Betts 2014-09-11 14:49:54 +01:00
parent c4e3c0c776
commit 16db58faba
5 changed files with 36 additions and 10 deletions

View File

@ -56,6 +56,23 @@ def _decorate_urlconf(urlpatterns, decorator, *args, **kwargs):
_decorate_urlconf(pattern.url_patterns, decorator, *args, **kwargs)
def access_cached(func):
def inner(self, context):
session = context['request'].session
try:
if session['allowed']['valid_for'] != session.get('token'):
raise KeyError()
except KeyError:
session['allowed'] = {"valid_for": session.get('token')}
key = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
if key not in session['allowed']:
session['allowed'][key] = func(self, context)
session.modified = True
return session['allowed'][key]
return inner
class NotRegistered(Exception):
pass
@ -90,8 +107,17 @@ class HorizonComponent(object):
urlpatterns = patterns('')
return urlpatterns
@access_cached
def can_access(self, context):
"""Checks to see that the user has role based access to this component.
"""Return whether the user has role based access to this component.
This method is not intended to be overridden.
The result of the method is stored in per-session cache.
"""
return self.allowed(context)
def allowed(self, context):
"""Checks if the user is allowed to access this component.
This method should be overridden to return the result of
any policy checks required for the user to access this component
@ -568,7 +594,7 @@ class Dashboard(Registry, HorizonComponent):
del loaders.panel_template_dirs[key]
return success
def can_access(self, context):
def allowed(self, context):
"""Checks for role based access for this dashboard.
Checks for access to any panels in the dashboard and of the the

View File

@ -57,7 +57,7 @@ class RbacNoAccessPanel(horizon.Panel):
name = "RBAC Panel No"
slug = "rbac_panel_no"
def _can_access(self, request):
def allowed(self, context):
return False
@ -508,7 +508,7 @@ class RbacHorizonTests(test.TestCase):
dash.register(panel)
def test_rbac_panels(self):
context = {'request': None}
context = {'request': self.request}
cats = horizon.get_dashboard("cats")
self.assertEqual(cats._registered_with, base.Horizon)
self.assertQuerysetEqual(cats.get_panels(),

View File

@ -23,7 +23,7 @@ class Firewall(horizon.Panel):
slug = "firewalls"
permissions = ('openstack.services.network',)
def can_access(self, context):
def allowed(self, context):
request = context['request']
if not request.user.has_perms(self.permissions):
return False
@ -31,7 +31,7 @@ class Firewall(horizon.Panel):
config_name='enable_firewall',
ext_name='fwaas'):
return False
if not super(Firewall, self).can_access(context):
if not super(Firewall, self).allowed(context):
return False
return True

View File

@ -23,7 +23,7 @@ class LoadBalancer(horizon.Panel):
slug = "loadbalancers"
permissions = ('openstack.services.network',)
def can_access(self, context):
def allowed(self, context):
request = context['request']
if not request.user.has_perms(self.permissions):
return False
@ -31,7 +31,7 @@ class LoadBalancer(horizon.Panel):
config_name='enable_lb',
ext_name='lbaas'):
return False
if not super(LoadBalancer, self).can_access(context):
if not super(LoadBalancer, self).allowed(context):
return False
return True

View File

@ -27,7 +27,7 @@ class VPN(horizon.Panel):
slug = 'vpn'
permissions = ('openstack.services.network',)
def can_access(self, context):
def allowed(self, context):
request = context['request']
if not request.user.has_perms(self.permissions):
return False
@ -35,7 +35,7 @@ class VPN(horizon.Panel):
config_name='enable_vpn',
ext_name='vpnaas'):
return False
if not super(VPN, self).can_access(context):
if not super(VPN, self).allowed(context):
return False
return True