Specify POLICY_CHECK_FUNCTION as a string

We don't want code in our settings.py and local_settings.py, and in
particular we don't want to have to import Python objects from all over
to set them as setting values -- instead, we can specify those as import
path strings. This also solves problems with importing order and loops.

This change is backwards-compatible, in the sense that you can still
import the objects directly and set them as the setting values.

Partially-Implements: blueprint ini-based-configuration
Change-Id: I8a346e55bb98e4e22e0c14a614c45d493d20feb4
This commit is contained in:
Radomir Dopieralski 2016-11-25 12:57:13 +01:00
parent 11f6e3de48
commit f5685ebe46
10 changed files with 58 additions and 38 deletions

View File

@ -45,6 +45,7 @@ from horizon.decorators import _current_component # noqa
from horizon.decorators import require_auth # noqa from horizon.decorators import require_auth # noqa
from horizon.decorators import require_perms # noqa from horizon.decorators import require_perms # noqa
from horizon import loaders from horizon import loaders
from horizon.utils import settings as utils_settings
# Name of the panel group for panels to be displayed without a group. # Name of the panel group for panels to be displayed without a group.
@ -144,7 +145,7 @@ class HorizonComponent(object):
return self._can_access(context['request']) return self._can_access(context['request'])
def _can_access(self, request): def _can_access(self, request):
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) policy_check = utils_settings.import_setting("POLICY_CHECK_FUNCTION")
# this check is an OR check rather than an AND check that is the # this check is an OR check rather than an AND check that is the
# default in the policy engine, so calling each rule individually # default in the policy engine, so calling each rule individually

View File

@ -33,6 +33,7 @@ import six
from horizon import messages from horizon import messages
from horizon.utils import functions from horizon.utils import functions
from horizon.utils import html from horizon.utils import html
from horizon.utils import settings as utils_settings
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -129,7 +130,7 @@ class BaseAction(html.HTMLElement):
return True return True
def _allowed(self, request, datum): def _allowed(self, request, datum):
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) policy_check = utils_settings.import_setting("POLICY_CHECK_FUNCTION")
if policy_check and self.policy_rules: if policy_check and self.policy_rules:
target = self.get_policy_target(request, datum) target = self.get_policy_target(request, datum)

28
horizon/utils/settings.py Normal file
View File

@ -0,0 +1,28 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
from django.conf import settings
from django.utils.module_loading import import_string
def import_object(name_or_object):
if isinstance(name_or_object, six.string_types):
return import_string(name_or_object)
return name_or_object
def import_setting(name):
"""Imports an object specified either directly or as a module path."""
value = getattr(settings, name, None)
return import_object(value)

View File

@ -27,7 +27,6 @@ from mox3.mox import IgnoreArg # noqa
from mox3.mox import IsA # noqa from mox3.mox import IsA # noqa
from horizon.workflows import views from horizon.workflows import views
from openstack_auth import policy as policy_backend
from openstack_dashboard import api from openstack_dashboard import api
from openstack_dashboard.dashboards.identity.projects import workflows from openstack_dashboard.dashboards.identity.projects import workflows
@ -100,7 +99,7 @@ class TenantsViewTests(test.BaseAdminViewTests):
class ProjectsViewNonAdminTests(test.TestCase): class ProjectsViewNonAdminTests(test.TestCase):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_stubs({api.keystone: ('tenant_list', @test.create_stubs({api.keystone: ('tenant_list',
'domain_lookup')}) 'domain_lookup')})
def test_index(self): def test_index(self):

View File

@ -12,13 +12,13 @@
# under the License. # under the License.
from django.conf import settings from horizon.utils import settings as utils_settings
def check(actions, request, target=None): def check(actions, request, target=None):
"""Wrapper of the configurable policy method.""" """Wrapper of the configurable policy method."""
policy_check = getattr(settings, "POLICY_CHECK_FUNCTION", None) policy_check = utils_settings.import_setting("POLICY_CHECK_FUNCTION")
if policy_check: if policy_check:
return policy_check(actions, request, target) return policy_check(actions, request, target)

View File

@ -305,14 +305,7 @@ THEME_COLLECTION_DIR = 'themes'
# Theme Cookie Name # Theme Cookie Name
THEME_COOKIE_NAME = 'theme' THEME_COOKIE_NAME = 'theme'
POLICY_CHECK_FUNCTION = 'openstack_auth.policy.check'
def check(actions, request, target=None):
# Note(Itxaka): This is to prevent circular dependencies and apps not ready
# If you do django imports in your settings, you are gonna have a bad time
from openstack_auth import policy
return policy.check(actions, request, target)
POLICY_CHECK_FUNCTION = check
CSRF_COOKIE_AGE = None CSRF_COOKIE_AGE = None

View File

@ -617,7 +617,7 @@ class NeutronApiTests(test.APITestCase):
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router': @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router':
True}, True},
POLICY_CHECK_FUNCTION=policy.check) POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_stubs({api.neutron: ('is_extension_supported',)}) @test.create_stubs({api.neutron: ('is_extension_supported',)})
def _test_get_dvr_permission_with_policy_check(self, policy_check_allowed, def _test_get_dvr_permission_with_policy_check(self, policy_check_allowed,
operation): operation):
@ -655,7 +655,7 @@ class NeutronApiTests(test.APITestCase):
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router': @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_distributed_router':
True}, True},
POLICY_CHECK_FUNCTION=policy.check) POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_get_dvr_permission_dvr_unsupported_operation(self): def test_get_dvr_permission_dvr_unsupported_operation(self):
self.assertRaises(ValueError, self.assertRaises(ValueError,
api.neutron.get_feature_permission, api.neutron.get_feature_permission,
@ -675,7 +675,7 @@ class NeutronApiTests(test.APITestCase):
# above. l3-ha check only checks l3-ha specific code. # above. l3-ha check only checks l3-ha specific code.
@override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_ha_router': True}, @override_settings(OPENSTACK_NEUTRON_NETWORK={'enable_ha_router': True},
POLICY_CHECK_FUNCTION=policy.check) POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
@test.create_stubs({api.neutron: ('is_extension_supported', )}) @test.create_stubs({api.neutron: ('is_extension_supported', )})
def _test_get_router_ha_permission_with_policy_check(self, ha_enabled): def _test_get_router_ha_permission_with_policy_check(self, ha_enabled):
self.mox.StubOutWithMock(policy, 'check') self.mox.StubOutWithMock(policy, 'check')

View File

@ -11,46 +11,45 @@
# limitations under the License. # limitations under the License.
from django.test.utils import override_settings # noqa from django.test.utils import override_settings # noqa
from openstack_auth import policy as policy_backend
from openstack_dashboard.api.rest import policy from openstack_dashboard.api.rest import policy
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
class PolicyRestTestCase(test.TestCase): class PolicyRestTestCase(test.TestCase):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_policy(self, body='{"rules": []}'): def test_policy(self, body='{"rules": []}'):
request = self.mock_rest_request(body=body) request = self.mock_rest_request(body=body)
response = policy.Policy().post(request) response = policy.Policy().post(request)
self.assertStatusCode(response, 200) self.assertStatusCode(response, 200)
self.assertEqual({"allowed": True}, response.json) self.assertEqual({"allowed": True}, response.json)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_rule_alone(self): def test_rule_alone(self):
body = '{"rules": [["compute", "compute:get_all" ]]}' body = '{"rules": [["compute", "compute:get_all" ]]}'
self.test_policy(body) self.test_policy(body)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_multiple_rule(self): def test_multiple_rule(self):
body = '{"rules": [["compute", "compute:get_all"],' \ body = '{"rules": [["compute", "compute:get_all"],' \
' ["compute", "compute:start"]]}' ' ["compute", "compute:start"]]}'
self.test_policy(body) self.test_policy(body)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_rule_with_empty_target(self): def test_rule_with_empty_target(self):
body = '{"rules": [["compute", "compute:get_all"],' \ body = '{"rules": [["compute", "compute:get_all"],' \
' ["compute", "compute:start"]],' \ ' ["compute", "compute:start"]],' \
' "target": {}}' ' "target": {}}'
self.test_policy(body) self.test_policy(body)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_rule_with_target(self): def test_rule_with_target(self):
body = '{"rules": [["compute", "compute:get_all"],' \ body = '{"rules": [["compute", "compute:get_all"],' \
' ["compute", "compute:start"]],' \ ' ["compute", "compute:start"]],' \
' "target": {"project_id": "1"}}' ' "target": {"project_id": "1"}}'
self.test_policy(body) self.test_policy(body)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_policy_fail(self): def test_policy_fail(self):
# admin only rule, default test case user should fail # admin only rule, default test case user should fail
request = self.mock_rest_request( request = self.mock_rest_request(
@ -59,7 +58,7 @@ class PolicyRestTestCase(test.TestCase):
self.assertStatusCode(response, 200) self.assertStatusCode(response, 200)
self.assertEqual({"allowed": False}, response.json) self.assertEqual({"allowed": False}, response.json)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_policy_error(self): def test_policy_error(self):
# admin only rule, default test case user should fail # admin only rule, default test case user should fail
request = self.mock_rest_request( request = self.mock_rest_request(
@ -69,7 +68,7 @@ class PolicyRestTestCase(test.TestCase):
class AdminPolicyRestTestCase(test.BaseAdminViewTests): class AdminPolicyRestTestCase(test.BaseAdminViewTests):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_rule_with_target(self): def test_rule_with_target(self):
body = '{"rules": [["compute", "compute:unlock_override"]]}' body = '{"rules": [["compute", "compute:unlock_override"]]}'
request = self.mock_rest_request(body=body) request = self.mock_rest_request(body=body)

View File

@ -12,14 +12,13 @@
# under the License. # under the License.
from django.test.utils import override_settings from django.test.utils import override_settings
from openstack_auth import policy as policy_backend
from openstack_dashboard import policy from openstack_dashboard import policy
from openstack_dashboard.test import helpers as test from openstack_dashboard.test import helpers as test
class PolicyTestCase(test.TestCase): class PolicyTestCase(test.TestCase):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_policy_check_set(self): def test_policy_check_set(self):
value = policy.check((("identity", "admin_required"),), value = policy.check((("identity", "admin_required"),),
request=self.request) request=self.request)
@ -33,7 +32,7 @@ class PolicyTestCase(test.TestCase):
class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests): class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_policy_check_set_admin(self): def test_policy_check_set_admin(self):
value = policy.check((("identity", "admin_required"),), value = policy.check((("identity", "admin_required"),),
request=self.request) request=self.request)

View File

@ -31,14 +31,14 @@ class PolicyBackendTestCase(test.TestCase):
policy_backend.reset() policy_backend.reset()
self.assertIsNone(policy_backend._ENFORCER) self.assertIsNone(policy_backend._ENFORCER)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_admin_required_false(self): def test_check_admin_required_false(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "admin_required"),), value = policy.check((("identity", "admin_required"),),
request=self.request) request=self.request)
self.assertFalse(value) self.assertFalse(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_identity_rule_not_found_false(self): def test_check_identity_rule_not_found_false(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "i_dont_exist"),), value = policy.check((("identity", "i_dont_exist"),),
@ -47,14 +47,14 @@ class PolicyBackendTestCase(test.TestCase):
# identity is admin_required # identity is admin_required
self.assertFalse(value) self.assertFalse(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_nova_context_is_admin_false(self): def test_check_nova_context_is_admin_false(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("compute", "context_is_admin"),), value = policy.check((("compute", "context_is_admin"),),
request=self.request) request=self.request)
self.assertFalse(value) self.assertFalse(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_compound_check_false(self): def test_compound_check_false(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "admin_required"), value = policy.check((("identity", "admin_required"),
@ -62,7 +62,7 @@ class PolicyBackendTestCase(test.TestCase):
request=self.request) request=self.request)
self.assertFalse(value) self.assertFalse(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_scope_not_found(self): def test_scope_not_found(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("dummy", "default"),), value = policy.check((("dummy", "default"),),
@ -71,14 +71,14 @@ class PolicyBackendTestCase(test.TestCase):
class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests): class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests):
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_admin_required_true(self): def test_check_admin_required_true(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "admin_required"),), value = policy.check((("identity", "admin_required"),),
request=self.request) request=self.request)
self.assertTrue(value) self.assertTrue(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_identity_rule_not_found_true(self): def test_check_identity_rule_not_found_true(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "i_dont_exist"),), value = policy.check((("identity", "i_dont_exist"),),
@ -87,7 +87,7 @@ class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests):
# identity is admin_required # identity is admin_required
self.assertTrue(value) self.assertTrue(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_compound_check_true(self): def test_compound_check_true(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("identity", "admin_required"), value = policy.check((("identity", "admin_required"),
@ -95,7 +95,7 @@ class PolicyBackendTestCaseAdmin(test.BaseAdminViewTests):
request=self.request) request=self.request)
self.assertTrue(value) self.assertTrue(value)
@override_settings(POLICY_CHECK_FUNCTION=policy_backend.check) @override_settings(POLICY_CHECK_FUNCTION='openstack_auth.policy.check')
def test_check_nova_context_is_admin_true(self): def test_check_nova_context_is_admin_true(self):
policy_backend.reset() policy_backend.reset()
value = policy.check((("compute", "context_is_admin"),), value = policy.check((("compute", "context_is_admin"),),