diff options
author | Adam Young <ayoung@redhat.com> | 2015-03-30 14:50:46 -0400 |
---|---|---|
committer | Adam Young <ayoung@redhat.com> | 2015-03-31 13:35:13 -0400 |
commit | a08bc79f5c117696c43feb2e44c4dc5fd0013deb (patch) | |
tree | 117aa4419169cf64ac93c5e4dc55c8c1e2147ca9 | |
parent | b3fe254031b511deb085073f50ae5978d216d78d (diff) | |
download | oslo-policy-a08bc79f5c117696c43feb2e44c4dc5fd0013deb.tar.gz |
Lists for Generic Checks
The Generic check had no way to identify
a value inside a list. Since Lists are inherantly unstable in
indexing, requiring an index to match is not practical.
Lists now follow the same approach as the OrCheck:
When specifying a value inside a list, each element
of the lsit is checked for a match. If any of the sub lists match,
the check succeeds. Only if the entry is not in the list does the
check fail.
If the value is nested in a dictionary under the list, all of
the subordinate dictionaries are checked in a recursive manner.
Change-Id: Ia286dbd3757703779d7044b3003381eab6c5c919
-rw-r--r-- | oslo_policy/_checks.py | 49 | ||||
-rw-r--r-- | oslo_policy/tests/test_checks.py | 52 | ||||
-rw-r--r-- | oslo_policy/tests/token_fixture.py | 164 |
3 files changed, 255 insertions, 10 deletions
diff --git a/oslo_policy/_checks.py b/oslo_policy/_checks.py index c0e4c4a..b011294 100644 --- a/oslo_policy/_checks.py +++ b/oslo_policy/_checks.py @@ -288,23 +288,52 @@ class GenericCheck(Check): - 'Member':%(role.name)s """ + def _find_in_dict(self, test_value, path_segments, match): + '''Searches for a match in the dictionary. + + test_value is a reference inside the dictionary. Since the process is + recursive, each call to _find_in_dict will be one level deeper. + + path_segments is the segments of the path to search. The recursion + ends when there are no more segments of path. + + When specifying a value inside a list, each element of the list is + checked for a match. If the value is found within any of the sub lists + the check succeeds; The check only fails if the entry is not in any of + the sublists. + + ''' + + if len(path_segments) == 0: + return match == six.text_type(test_value) + key, path_segments = path_segments[0], path_segments[1:] + try: + test_value = test_value[key] + except KeyError: + return False + if isinstance(test_value, list): + for val in test_value: + if self._find_in_dict(val, path_segments, match): + return True + return False + else: + return self._find_in_dict(test_value, path_segments, match) + def __call__(self, target, creds, enforcer): + try: match = self.match % target except KeyError: # While doing GenericCheck if key not # present in Target return false return False - try: # Try to interpret self.kind as a literal - leftval = ast.literal_eval(self.kind) + test_value = ast.literal_eval(self.kind) + return match == six.text_type(test_value) + except ValueError: - try: - kind_parts = self.kind.split('.') - leftval = creds - for kind_part in kind_parts: - leftval = leftval[kind_part] - except KeyError: - return False - return match == six.text_type(leftval) + pass + + path_segments = self.kind.split('.') + return self._find_in_dict(creds, path_segments, match) diff --git a/oslo_policy/tests/test_checks.py b/oslo_policy/tests/test_checks.py index 32def91..31144c4 100644 --- a/oslo_policy/tests/test_checks.py +++ b/oslo_policy/tests/test_checks.py @@ -23,6 +23,7 @@ import six.moves.urllib.request as urlrequest from oslo_policy import _checks from oslo_policy.tests import base +from oslo_policy.tests import token_fixture class CheckRegisterTestCase(test_base.BaseTestCase): @@ -220,6 +221,57 @@ class GenericCheckTestCase(base.PolicyBaseTestCase): 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): diff --git a/oslo_policy/tests/token_fixture.py b/oslo_policy/tests/token_fixture.py new file mode 100644 index 0000000..e609187 --- /dev/null +++ b/oslo_policy/tests/token_fixture.py @@ -0,0 +1,164 @@ +# 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. + +REGION_ONE_PUBLIC_KEYSTONE_ENDPOINT_ID = '8cd4b957090f4ca5842a22e9a74099cd' + + +SCOPED_TOKEN_FIXTURE = { + "token": { + "methods": [ + "password" + ], + "roles": [ + { + "id": "f03fda8f8a3249b2a70fb1f176a7b631", + "name": "role1" + }, + { + "id": "f03fda8f8a3249b2a70fb1f176a7b631", + "name": "role2" + } + ], + "issued_at": "2002-01-18T21:14:07Z", + "expires_at": "2038-01-18T21:14:07Z", + "project": { + "id": "tenant_id1", + "domain": { + "id": "domain_id1", + "name": "domain_name1" + }, + "enabled": True, + "description": "no description avialable", + "name": "tenant_name1" + }, + "catalog": [ + { + "endpoints": [ + { + "id": "3b5e554bcf114f2483e8a1be7a0506d1", + "interface": "admin", + "url": "http://127.0.0.1:8776/v1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + }, + { + "id": "54abd2dc463c4ba4a72915498f8ecad1", + "interface": "internal", + "url": "http://127.0.0.1:8776/v1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + }, + { + "id": "70a7efa4b1b941968357cc43ae1419ee", + "interface": "public", + "url": "http://127.0.0.1:8776/v1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + } + ], + "id": "5707c3fc0a294703a3c638e9cf6a6c3a", + "type": "volume", + "name": "volume" + }, + { + "endpoints": [ + { + "id": "92217a3b95394492859bc49fd474382f", + "interface": "admin", + "url": "http://127.0.0.1:9292/v1", + "region": "regionOne" + }, + { + "id": "f20563bdf66f4efa8a1f11d99b672be1", + "interface": "internal", + "url": "http://127.0.0.1:9292/v1", + "region": "regionOne" + }, + { + "id": "375f9ba459a447738fb60fe5fc26e9aa", + "interface": "public", + "url": "http://127.0.0.1:9292/v1", + "region": "regionOne" + } + ], + "id": "15c21aae6b274a8da52e0a068e908aac", + "type": "image", + "name": "glance" + }, + { + "endpoints": [ + { + "id": "edbd9f50f66746ae9ed11dc3b1ae35da", + "interface": "admin", + "url": "http://127.0.0.1:8774/v1.1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + }, + { + "id": "9e03c46c80a34a159cb39f5cb0498b92", + "interface": "internal", + "url": "http://127.0.0.1:8774/v1.1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + }, + { + "id": "1df0b44d92634d59bd0e0d60cf7ce432", + "interface": "public", + "url": + "http://127.0.0.1:8774/v1.1/" + + "64b6f3fbcc53435e8a60fcf89bb6617a", + "region": "regionOne" + } + ], + "id": "2f404fdb89154c589efbc10726b029ec", + "type": "compute", + "name": "nova" + }, + { + "endpoints": [ + { + "id": "a4501e141a4b4e14bf282e7bffd81dc5", + "interface": "admin", + "url": "http://127.0.0.1:35357/v3", + "region": "RegionOne" + }, + { + "id": "3d17e3227bfc4483b58de5eaa584e360", + "interface": "internal", + "url": "http://127.0.0.1:35357/v3", + "region": "RegionOne" + }, + { + "id": REGION_ONE_PUBLIC_KEYSTONE_ENDPOINT_ID, + "interface": "public", + "url": "http://127.0.0.1:5000/v3", + "region": "RegionOne" + } + ], + "id": "c5d926d566424e4fba4f80c37916cde5", + "type": "identity", + "name": "keystone" + } + ], + "user": { + "domain": { + "id": "domain_id1", + "name": "domain_name1" + }, + "name": "user_name1", + "id": "user_id1" + } + } +} |