oslo.policy/oslo_policy/tests/test_checks.py
Davanum Srinivas 7e185ad96b http/https check rules as stevedore extensions
Why? HttpCheck/HttpsCheck are examples of rule checks
that can be implemented outside of the oslo.policy
library. Once we setup the infra for registering and
using these as stevedore extensions, we automatically
get the capability of other folks contributing to
writing custom rules for their own use cases.

* Add HttpCheck/HttpsCheck as entrypoints in setup.cfg
* parser will check get_extensions() to see if there
  are any external checks registered
* Move HttpCheck/HttpsCheck into external module
* Move related test cases to test_external.py

Change-Id: Icde2b26a38d7c7842defae053228d9208454b969
2017-10-05 08:40:47 -04:00

498 lines
16 KiB
Python

# Copyright (c) 2015 OpenStack Foundation.
# All Rights Reserved.
# 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 mock
from oslotest import base as test_base
from oslo_policy import _checks
from oslo_policy.tests import base
from oslo_policy.tests import token_fixture
class CheckRegisterTestCase(test_base.BaseTestCase):
@mock.patch.object(_checks, 'registered_checks', {})
def test_register_check(self):
class TestCheck(_checks.Check):
pass
_checks.register('spam', TestCheck)
self.assertEqual(dict(spam=TestCheck), _checks.registered_checks)
@mock.patch.object(_checks, 'registered_checks', {})
def test_register_check_decorator(self):
@_checks.register('spam')
class TestCheck(_checks.Check):
pass
self.assertEqual(dict(spam=TestCheck), _checks.registered_checks)
class RuleCheckTestCase(base.PolicyBaseTestCase):
def test_rule_missing(self):
self.enforcer.rules = {}
check = _checks.RuleCheck('rule', 'spam')
self.assertFalse(check('target', 'creds', self.enforcer))
def test_rule_false(self):
self.enforcer.rules = dict(spam=_BoolCheck(False))
check = _checks.RuleCheck('rule', 'spam')
self.assertFalse(check('target', 'creds', self.enforcer))
def test_rule_true(self):
self.enforcer.rules = dict(spam=_BoolCheck(True))
check = _checks.RuleCheck('rule', 'spam')
self.assertTrue(check('target', 'creds', self.enforcer))
class RoleCheckTestCase(base.PolicyBaseTestCase):
def test_accept(self):
check = _checks.RoleCheck('role', 'sPaM')
self.assertTrue(check({}, dict(roles=['SpAm']), self.enforcer))
def test_reject(self):
check = _checks.RoleCheck('role', 'spam')
self.assertFalse(check({}, dict(roles=[]), self.enforcer))
def test_format_value(self):
check = _checks.RoleCheck('role', '%(target.role.name)s')
target_dict = {'target.role.name': 'a'}
cred_dict = dict(user='user', roles=['a', 'b', 'c'])
self.assertTrue(check(target_dict, cred_dict, self.enforcer))
target_dict = {'target.role.name': 'd'}
self.assertFalse(check(target_dict, cred_dict, self.enforcer))
target_dict = dict(target=dict(role=dict()))
self.assertFalse(check(target_dict, cred_dict, self.enforcer))
def test_no_roles_case(self):
check = _checks.RoleCheck('role', 'spam')
self.assertFalse(check({}, {}, self.enforcer))
class GenericCheckTestCase(base.PolicyBaseTestCase):
def test_no_cred(self):
check = _checks.GenericCheck('name', '%(name)s')
self.assertFalse(check(dict(name='spam'), {}, self.enforcer))
def test_cred_mismatch(self):
check = _checks.GenericCheck('name', '%(name)s')
self.assertFalse(check(dict(name='spam'),
dict(name='ham'),
self.enforcer))
def test_accept(self):
check = _checks.GenericCheck('name', '%(name)s')
self.assertTrue(check(dict(name='spam'),
dict(name='spam'),
self.enforcer))
def test_no_key_match_in_target(self):
check = _checks.GenericCheck('name', '%(name)s')
self.assertFalse(check(dict(name1='spam'),
dict(name='spam'),
self.enforcer))
def test_constant_string_mismatch(self):
check = _checks.GenericCheck("'spam'", '%(name)s')
self.assertFalse(check(dict(name='ham'), {}, self.enforcer))
def test_constant_string_accept(self):
check = _checks.GenericCheck("'spam'", '%(name)s')
self.assertTrue(check(dict(name='spam'), {}, self.enforcer))
def test_constant_literal_mismatch(self):
check = _checks.GenericCheck('True', '%(enabled)s')
self.assertFalse(check(dict(enabled=False), {}, self.enforcer))
def test_constant_literal_accept(self):
check = _checks.GenericCheck('True', '%(enabled)s')
self.assertTrue(check(dict(enabled=True), {}, self.enforcer))
def test_deep_credentials_dictionary_lookup(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': {'c': {'d': 'APPLES'}}}}
self.assertTrue(check({}, credentials, self.enforcer))
def test_missing_credentials_dictionary_lookup(self):
credentials = {'a': 'APPLES', 'o': {'t': 'ORANGES'}}
# First a valid check - rest of case is expecting failures
# Should prove the basic credentials structure before we test
# for failure cases.
check = _checks.GenericCheck('o.t', 'ORANGES')
self.assertTrue(check({}, credentials, self.enforcer))
# Case where final key is missing
check = _checks.GenericCheck('o.v', 'ORANGES')
self.assertFalse(check({}, credentials, self.enforcer))
# Attempt to access key under a missing dictionary
check = _checks.GenericCheck('q.v', 'APPLES')
self.assertFalse(check({}, credentials, self.enforcer))
def test_single_entry_in_list_accepted(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': {'c': {'d': ['APPLES']}}}}
self.assertTrue(check({}, credentials, self.enforcer))
def test_multiple_entry_in_list_accepted(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': {'c': {'d': ['Bananas',
'APPLES',
'Grapes']}}}}
self.assertTrue(check({}, credentials, self.enforcer))
def test_multiple_entry_in_nested_list_accepted(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': [{'c':
{'d': ['BANANAS', 'APPLES', 'GRAPES']}},
{}]}}
self.assertTrue(check({}, credentials, self.enforcer))
def test_multiple_entries_one_matches(self):
check = _checks.GenericCheck(
'token.catalog.endpoints.id',
token_fixture.REGION_ONE_PUBLIC_KEYSTONE_ENDPOINT_ID)
credentials = token_fixture.SCOPED_TOKEN_FIXTURE
self.assertTrue(check({}, credentials, self.enforcer))
def test_generic_role_check_matches(self):
check = _checks.GenericCheck(
'token.roles.name', 'role1')
credentials = token_fixture.SCOPED_TOKEN_FIXTURE
self.assertTrue(check({}, credentials, self.enforcer))
def test_generic_missing_role_does_not_matches(self):
check = _checks.GenericCheck(
'token.roles.name', 'missing')
credentials = token_fixture.SCOPED_TOKEN_FIXTURE
self.assertFalse(check({}, credentials, self.enforcer))
def test_multiple_nested_lists_accepted(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': [{'a': ''},
{'c':
{'d': ['BANANAS', 'APPLES', 'GRAPES']}},
{}]}}
self.assertTrue(check({}, credentials, self.enforcer))
def test_entry_not_in_list_rejected(self):
check = _checks.GenericCheck('a.b.c.d', 'APPLES')
credentials = {'a': {'b': {'c': {'d': ['PEACHES', 'PEARS']}}}}
self.assertFalse(check({}, credentials, self.enforcer))
class FalseCheckTestCase(test_base.BaseTestCase):
def test_str(self):
check = _checks.FalseCheck()
self.assertEqual('!', str(check))
def test_call(self):
check = _checks.FalseCheck()
self.assertFalse(check('target', 'creds', None))
class TrueCheckTestCase(test_base.BaseTestCase):
def test_str(self):
check = _checks.TrueCheck()
self.assertEqual('@', str(check))
def test_call(self):
check = _checks.TrueCheck()
self.assertTrue(check('target', 'creds', None))
class CheckForTest(_checks.Check):
def __call__(self, target, creds, enforcer):
pass
class CheckTestCase(test_base.BaseTestCase):
def test_init(self):
check = CheckForTest('kind', 'match')
self.assertEqual('kind', check.kind)
self.assertEqual('match', check.match)
def test_str(self):
check = CheckForTest('kind', 'match')
self.assertEqual('kind:match', str(check))
class NotCheckTestCase(test_base.BaseTestCase):
def test_init(self):
check = _checks.NotCheck('rule')
self.assertEqual('rule', check.rule)
def test_str(self):
check = _checks.NotCheck('rule')
self.assertEqual('not rule', str(check))
def test_call_true(self):
rule = _checks.TrueCheck()
check = _checks.NotCheck(rule)
self.assertFalse(check('target', 'cred', None))
def test_call_false(self):
rule = _checks.FalseCheck()
check = _checks.NotCheck(rule)
self.assertTrue(check('target', 'cred', None))
def test_rule_takes_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer, current_rule=None):
results.append((target, cred, enforcer, current_rule))
return True
check = _checks.NotCheck(TestCheck())
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None, 'a_rule')],
results,
)
def test_rule_does_not_take_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer):
results.append((target, cred, enforcer))
return True
check = _checks.NotCheck(TestCheck())
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None)],
results,
)
class _BoolCheck(_checks.BaseCheck):
def __init__(self, result):
self.called = False
self.result = result
def __str__(self):
return str(self.result)
def __call__(self, target, creds, enforcer, current_rule=None):
self.called = True
return self.result
class AndCheckTestCase(test_base.BaseTestCase):
def test_init(self):
check = _checks.AndCheck(['rule1', 'rule2'])
self.assertEqual(['rule1', 'rule2'], check.rules)
def test_add_check(self):
check = _checks.AndCheck(['rule1', 'rule2'])
check.add_check('rule3')
self.assertEqual(['rule1', 'rule2', 'rule3'], check.rules)
def test_str(self):
check = _checks.AndCheck(['rule1', 'rule2'])
self.assertEqual('(rule1 and rule2)', str(check))
def test_call_all_false(self):
rules = [
_BoolCheck(False),
_BoolCheck(False),
]
check = _checks.AndCheck(rules)
self.assertFalse(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertFalse(rules[1].called)
def test_call_first_true(self):
rules = [
_BoolCheck(True),
_BoolCheck(False),
]
check = _checks.AndCheck(rules)
self.assertFalse(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertTrue(rules[1].called)
def test_call_second_true(self):
rules = [
_BoolCheck(False),
_BoolCheck(True),
]
check = _checks.AndCheck(rules)
self.assertFalse(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertFalse(rules[1].called)
def test_rule_takes_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer, current_rule=None):
results.append((target, cred, enforcer, current_rule))
return False
check = _checks.AndCheck([TestCheck()])
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None, 'a_rule')],
results,
)
def test_rule_does_not_take_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer):
results.append((target, cred, enforcer))
return False
check = _checks.AndCheck([TestCheck()])
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None)],
results,
)
class OrCheckTestCase(test_base.BaseTestCase):
def test_init(self):
check = _checks.OrCheck(['rule1', 'rule2'])
self.assertEqual(['rule1', 'rule2'], check.rules)
def test_add_check(self):
check = _checks.OrCheck(['rule1', 'rule2'])
check.add_check('rule3')
self.assertEqual(['rule1', 'rule2', 'rule3'], check.rules)
def test_pop_check(self):
check = _checks.OrCheck(['rule1', 'rule2', 'rule3'])
rules, check1 = check.pop_check()
self.assertEqual(['rule1', 'rule2'], check.rules)
self.assertEqual('rule3', check1)
def test_str(self):
check = _checks.OrCheck(['rule1', 'rule2'])
self.assertEqual('(rule1 or rule2)', str(check))
def test_call_all_false(self):
rules = [
_BoolCheck(False),
_BoolCheck(False),
]
check = _checks.OrCheck(rules)
self.assertFalse(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertTrue(rules[1].called)
def test_call_first_true(self):
rules = [
_BoolCheck(True),
_BoolCheck(False),
]
check = _checks.OrCheck(rules)
self.assertTrue(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertFalse(rules[1].called)
def test_call_second_true(self):
rules = [
_BoolCheck(False),
_BoolCheck(True),
]
check = _checks.OrCheck(rules)
self.assertTrue(check('target', 'cred', None))
self.assertTrue(rules[0].called)
self.assertTrue(rules[1].called)
def test_rule_takes_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer, current_rule=None):
results.append((target, cred, enforcer, current_rule))
return False
check = _checks.OrCheck([TestCheck()])
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None, 'a_rule')],
results,
)
def test_rule_does_not_take_current_rule(self):
results = []
class TestCheck(object):
def __call__(self, target, cred, enforcer):
results.append((target, cred, enforcer))
return False
check = _checks.OrCheck([TestCheck()])
self.assertFalse(check('target', 'cred', None, current_rule="a_rule"))
self.assertEqual(
[('target', 'cred', None)],
results,
)