summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Young <ayoung@redhat.com>2015-03-30 14:50:46 -0400
committerAdam Young <ayoung@redhat.com>2015-03-31 13:35:13 -0400
commita08bc79f5c117696c43feb2e44c4dc5fd0013deb (patch)
tree117aa4419169cf64ac93c5e4dc55c8c1e2147ca9
parentb3fe254031b511deb085073f50ae5978d216d78d (diff)
downloadoslo-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.py49
-rw-r--r--oslo_policy/tests/test_checks.py52
-rw-r--r--oslo_policy/tests/token_fixture.py164
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"
+ }
+ }
+}