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
This commit is contained in:
Adam Young 2015-03-30 14:50:46 -04:00
parent b3fe254031
commit a08bc79f5c
3 changed files with 255 additions and 10 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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"
}
}
}