Modernize policy checker

Without this patch, the policy checker issues a 'failed' result when
checking a system-scoped sample token against a policy string like
"role:admin and system_scope:all", because the policy checker does not
understand the 'system_scope' attribute that is now in oslo.context[1]
and wasn't parsing the "system" scope object from the sample token.
Similarly, it fails on a string like "user_id:%(user_id)s" because it
never looked up the user_id from the sample token. This change updates
the policy checker to understand token contexts and policies like these
so that more of the policy defaults in keystone, and soon other
projects, will pass. This also adds a new system-scoped sample token to
check against.

[1] https://review.opendev.org/530509

Change-Id: I02fbbc99d28aa5c787133f530f6e968341107bf7
This commit is contained in:
Colleen Murphy 2019-09-17 20:36:32 -07:00
parent e433a3cbec
commit 99daead510
5 changed files with 356 additions and 11 deletions

View File

@ -74,7 +74,11 @@ def tool(policy_file, access_file, apply_rule, is_admin=False,
access_data = jsonutils.loads(access)['token']
access_data['roles'] = [role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
if access_data.get('project'):
access_data['project_id'] = access_data['project']['id']
if access_data.get('system'):
access_data['system_scope'] = 'all'
access_data['is_admin'] = is_admin
with open(policy_file, "rb", 0) as p:
@ -90,7 +94,9 @@ def tool(policy_file, access_file, apply_rule, is_admin=False,
target_data = flatten(jsonutils.loads(target))
else:
target_data = {"project_id": access_data['project_id']}
target_data = {'user_id': access_data['user']['id']}
if access_data.get('project_id'):
target_data['project_id'] = access_data['project_id']
if apply_rule:
key = apply_rule

View File

@ -185,19 +185,19 @@ class GenericCheckTestCase(base.PolicyBaseTestCase):
check = _checks.GenericCheck(
'token.catalog.endpoints.id',
token_fixture.REGION_ONE_PUBLIC_KEYSTONE_ENDPOINT_ID)
credentials = token_fixture.SCOPED_TOKEN_FIXTURE
credentials = token_fixture.PROJECT_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
credentials = token_fixture.PROJECT_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
credentials = token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE
self.assertFalse(check({}, credentials, self.enforcer))
def test_multiple_nested_lists_accepted(self):

View File

@ -34,6 +34,15 @@ class CheckerTestCase(base.PolicyBaseTestCase):
"sampleservice:sample_rule2": ""
"sampleservice:sample_rule0": ""
"sampleservice:sample_rule1": ""
'''
SAMPLE_POLICY_SCOPED = '''---
"sampleservice:sample_rule": "role:role1"
"sampleservice:scoped_rule": "role:role1 and system_scope:all"
'''
SAMPLE_POLICY_OWNER = '''---
"sampleservice:owner_rule": "user_id:%(user_id)s"
'''
def setUp(self):
@ -41,7 +50,7 @@ class CheckerTestCase(base.PolicyBaseTestCase):
self.create_config_file("policy.yaml", self.SAMPLE_POLICY)
self.create_config_file(
"access.json",
jsonutils.dumps(token_fixture.SCOPED_TOKEN_FIXTURE))
jsonutils.dumps(token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE))
@mock.patch("oslo_policy._checks.TrueCheck.__call__")
def test_pass_rule_parameters(self, call_mock):
@ -53,12 +62,14 @@ class CheckerTestCase(base.PolicyBaseTestCase):
stdout = self._capture_stdout()
access_data = copy.deepcopy(
token_fixture.SCOPED_TOKEN_FIXTURE["token"])
token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE["token"])
target = {
"project_id": access_data['project']['id']
'user_id': access_data['user']['id'],
'project_id': access_data['project']['id']
}
access_data['roles'] = [
role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
access_data['project_id'] = access_data['project']['id']
access_data['is_admin'] = is_admin
@ -71,6 +82,56 @@ class CheckerTestCase(base.PolicyBaseTestCase):
'''
self.assertEqual(expected, stdout.getvalue())
def test_pass_rule_parameters_with_scope(self):
self.create_config_file("policy.yaml", self.SAMPLE_POLICY_SCOPED)
self.create_config_file(
"access.json",
jsonutils.dumps(token_fixture.SYSTEM_SCOPED_TOKEN_FIXTURE))
policy_file = self.get_config_file_fullname('policy.yaml')
access_file = self.get_config_file_fullname('access.json')
apply_rule = None
is_admin = False
stdout = self._capture_stdout()
access_data = copy.deepcopy(
token_fixture.SYSTEM_SCOPED_TOKEN_FIXTURE["token"])
access_data['roles'] = [
role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
access_data['is_admin'] = is_admin
shell.tool(policy_file, access_file, apply_rule, is_admin)
expected = '''passed: sampleservice:sample_rule
passed: sampleservice:scoped_rule
'''
self.assertEqual(expected, stdout.getvalue())
def test_pass_rule_parameters_with_owner(self):
self.create_config_file("policy.yaml", self.SAMPLE_POLICY_OWNER)
self.create_config_file(
"access.json",
jsonutils.dumps(token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE))
policy_file = self.get_config_file_fullname('policy.yaml')
access_file = self.get_config_file_fullname('access.json')
apply_rule = None
is_admin = False
stdout = self._capture_stdout()
access_data = copy.deepcopy(
token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE["token"])
access_data['roles'] = [
role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
access_data['project_id'] = access_data['project']['id']
access_data['is_admin'] = is_admin
shell.tool(policy_file, access_file, apply_rule, is_admin)
expected = '''passed: sampleservice:owner_rule
'''
self.assertEqual(expected, stdout.getvalue())
def test_pass_rule_parameters_sorted(self):
self.create_config_file("policy.yaml", self.SAMPLE_POLICY_UNSORTED)
@ -81,9 +142,10 @@ class CheckerTestCase(base.PolicyBaseTestCase):
stdout = self._capture_stdout()
access_data = copy.deepcopy(
token_fixture.SCOPED_TOKEN_FIXTURE["token"])
token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE["token"])
access_data['roles'] = [
role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
access_data['project_id'] = access_data['project']['id']
access_data['is_admin'] = is_admin
@ -100,9 +162,10 @@ passed: sampleservice:sample_rule2
apply_rule = None
is_admin = False
access_data = copy.deepcopy(
token_fixture.SCOPED_TOKEN_FIXTURE["token"])
token_fixture.PROJECT_SCOPED_TOKEN_FIXTURE["token"])
access_data['roles'] = [
role['name'] for role in access_data['roles']]
access_data['user_id'] = access_data['user']['id']
access_data['project_id'] = access_data['project']['id']
access_data['is_admin'] = is_admin

View File

@ -16,7 +16,7 @@
REGION_ONE_PUBLIC_KEYSTONE_ENDPOINT_ID = '8cd4b957090f4ca5842a22e9a74099cd'
SCOPED_TOKEN_FIXTURE = {
PROJECT_SCOPED_TOKEN_FIXTURE = {
"token": {
"methods": [
"password"
@ -162,3 +162,143 @@ SCOPED_TOKEN_FIXTURE = {
}
}
}
SYSTEM_SCOPED_TOKEN_FIXTURE = {
"token": {
"methods": [
"password"
],
"expires_at": "2038-01-18T21:14:07Z",
"issued_at": "2000-01-18T21:14:07Z",
"roles": [
{
"id": "41b1af9bb39241e8b8b79fae5906abcc",
"name": "role1"
},
{
"id": "ac9add6b3c5a46dcaaf21390c4657949",
"name": "role2"
}
],
"system": {
"all": True
},
"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"
}
}
}

View File

@ -0,0 +1,136 @@
{
"token": {
"methods": [
"password"
],
"expires_at": "2038-01-18T21:14:07Z",
"issued_at": "2000-01-18T21:14:07Z",
"roles": [
{
"id":"41b1af9bb39241e8b8b79fae5906abcc",
"name": "admin"
},
{
"id": "ac9add6b3c5a46dcaaf21390c4657949",
"name": "member"
},
{
"id": "b0cb8117845f4fd489865d498b80bab3",
"name": "reader"
}
],
"system": {
"all": true
},
"catalog": [
{
"endpoints": [
{
"id": "f84e070735e54914b41e2b5cfa94dcf7",
"interface": "admin",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "8220bba1d2844e0b81b171c6ede1155f",
"interface": "internal",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "719b92ea82a04e7a9ff1107c62da10da",
"interface": "public",
"url": "http://127.0.0.1:8776/v1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"type": "volume",
"name": "volume",
"id":"547e9195d1914b5eb087bedbc98fccc3"
},
{
"endpoints": [
{
"id": "44752324c0d44375bc854168ea22f1fc",
"interface": "admin",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "a59b3734f57449078f1637c10f96c8e8",
"interface": "internal",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
},
{
"id": "16c3ab1a4df640569812e432c98b2a48",
"interface": "public",
"url": "http://127.0.0.1:9292/v1",
"region": "regionOne"
}
],
"type": "image",
"name": "glance",
"id": "22c15d232e55419eb4aeb3ebbd12aac2"
},
{
"endpoints": [
{
"id": "9c2fdc2d45bb45c5a7f973e235e0f998",
"interface": "admin",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "88ccfa8cbb7743998b38b998f4e6a720",
"interface": "internal",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
},
{
"id": "113ee928c6934c92b9a12bd4e456c804",
"interface": "public",
"url": "http://127.0.0.1:8774/v1.1/64b6f3fbcc53435e8a60fcf89bb6617a",
"region": "regionOne"
}
],
"type": "compute",
"name": "nova",
"id": "fbf2afcdeb10473392636df9785d3fb5"
},
{
"endpoints": [
{
"id": "c10a5cda00784049953296d18464aa38",
"interface": "admin",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "334650263e064428bb2f0b7c3c7a743c",
"interface": "internal",
"url": "http://127.0.0.1:35357/v3",
"region": "RegionOne"
},
{
"id": "52ff54addc38430d9b656c7164e2caf8",
"interface": "public",
"url": "http://127.0.0.1:5000/v3",
"region": "RegionOne"
}
],
"type": "identity",
"name": "keystone",
"id": "a0d9913a4bca4d5699e151804e0b5172"
}
],
"user": {
"domain": {
"id": "domain_id1",
"name": "domain_name1"
},
"name": "user_name1",
"id": "user_id1"
}
}
}