Merge "Implement system reader role for trusts API"

This commit is contained in:
Zuul 2019-09-08 05:59:34 +00:00 committed by Gerrit Code Review
commit 43f9bf7432
2 changed files with 210 additions and 44 deletions

View File

@ -10,12 +10,43 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from oslo_log import versionutils
from oslo_policy import policy from oslo_policy import policy
from keystone.common.policies import base from keystone.common.policies import base
RULE_TRUSTOR = 'user_id:%(target.trust.trustor_user_id)s' RULE_TRUSTOR = 'user_id:%(target.trust.trustor_user_id)s'
RULE_TRUSTEE = 'user_id:%(target.trust.trustee_user_id)s' RULE_TRUSTEE = 'user_id:%(target.trust.trustee_user_id)s'
SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE = (
base.SYSTEM_READER + ' or ' + RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
)
SYSTEM_READER_OR_TRUSTOR = base.SYSTEM_READER + ' or ' + RULE_TRUSTOR
SYSTEM_READER_OR_TRUSTEE = base.SYSTEM_READER + ' or ' + RULE_TRUSTEE
deprecated_list_trusts = policy.DeprecatedRule(
name=base.IDENTITY % 'list_trusts',
check_str=base.RULE_ADMIN_REQUIRED
)
deprecated_list_roles_for_trust = policy.DeprecatedRule(
name=base.IDENTITY % 'list_roles_for_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
)
deprecated_get_role_for_trust = policy.DeprecatedRule(
name=base.IDENTITY % 'get_role_for_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
)
deprecated_get_trust = policy.DeprecatedRule(
name=base.IDENTITY % 'get_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE
)
DEPRECATED_REASON = """
As of the Train release, the trust API now understands default roles and
system-scoped tokens, making the API more granular by default without
compromising security. The new policy defaults account for these changes
automatically. Be sure to take these new defaults into consideration if you are
relying on overrides in your deployment for the service API.
"""
trust_policies = [ trust_policies = [
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
@ -30,17 +61,20 @@ trust_policies = [
'method': 'POST'}]), 'method': 'POST'}]),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_trusts', name=base.IDENTITY % 'list_trusts',
check_str=base.RULE_ADMIN_REQUIRED, check_str=base.SYSTEM_READER,
scope_types=['project'], scope_types=['system'],
description='List trusts.', description='List trusts.',
operations=[{'path': '/v3/OS-TRUST/trusts', operations=[{'path': '/v3/OS-TRUST/trusts',
'method': 'GET'}, 'method': 'GET'},
{'path': '/v3/OS-TRUST/trusts', {'path': '/v3/OS-TRUST/trusts',
'method': 'HEAD'}]), 'method': 'HEAD'}],
deprecated_rule=deprecated_list_trusts,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.TRAIN),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_trusts_for_trustor', name=base.IDENTITY % 'list_trusts_for_trustor',
check_str=RULE_TRUSTOR, check_str=SYSTEM_READER_OR_TRUSTOR,
scope_types=['project'], scope_types=['system', 'project'],
description='List trusts for trustor.', description='List trusts for trustor.',
operations=[{'path': '/v3/OS-TRUST/trusts?trustor_user_id={trustor_user_id}', operations=[{'path': '/v3/OS-TRUST/trusts?trustor_user_id={trustor_user_id}',
'method': 'GET'}, 'method': 'GET'},
@ -48,8 +82,8 @@ trust_policies = [
'method': 'HEAD'}]), 'method': 'HEAD'}]),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_trusts_for_trustee', name=base.IDENTITY % 'list_trusts_for_trustee',
check_str=RULE_TRUSTEE, check_str=SYSTEM_READER_OR_TRUSTEE,
scope_types=['project'], scope_types=['system', 'project'],
description='List trusts for trustee.', description='List trusts for trustee.',
operations=[{'path': '/v3/OS-TRUST/trusts?trustee_user_id={trustee_user_id}', operations=[{'path': '/v3/OS-TRUST/trusts?trustee_user_id={trustee_user_id}',
'method': 'GET'}, 'method': 'GET'},
@ -57,22 +91,28 @@ trust_policies = [
'method': 'HEAD'}]), 'method': 'HEAD'}]),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'list_roles_for_trust', name=base.IDENTITY % 'list_roles_for_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE, check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
scope_types=['project'], scope_types=['system', 'project'],
description='List roles delegated by a trust.', description='List roles delegated by a trust.',
operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles', operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles',
'method': 'GET'}, 'method': 'GET'},
{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles', {'path': '/v3/OS-TRUST/trusts/{trust_id}/roles',
'method': 'HEAD'}]), 'method': 'HEAD'}],
deprecated_rule=deprecated_list_roles_for_trust,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.TRAIN),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_role_for_trust', name=base.IDENTITY % 'get_role_for_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE, check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
scope_types=['project'], scope_types=['system', 'project'],
description='Check if trust delegates a particular role.', description='Check if trust delegates a particular role.',
operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}', operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}',
'method': 'GET'}, 'method': 'GET'},
{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}', {'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}',
'method': 'HEAD'}]), 'method': 'HEAD'}],
deprecated_rule=deprecated_get_role_for_trust,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.TRAIN),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'delete_trust', name=base.IDENTITY % 'delete_trust',
check_str=RULE_TRUSTOR, check_str=RULE_TRUSTOR,
@ -82,13 +122,16 @@ trust_policies = [
'method': 'DELETE'}]), 'method': 'DELETE'}]),
policy.DocumentedRuleDefault( policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_trust', name=base.IDENTITY % 'get_trust',
check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE, check_str=SYSTEM_READER_OR_TRUSTOR_OR_TRUSTEE,
scope_types=['project'], scope_types=['system', 'project'],
description='Get trust.', description='Get trust.',
operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}', operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}',
'method': 'GET'}, 'method': 'GET'},
{'path': '/v3/OS-TRUST/trusts/{trust_id}', {'path': '/v3/OS-TRUST/trusts/{trust_id}',
'method': 'HEAD'}]) 'method': 'HEAD'}],
deprecated_rule=deprecated_get_trust,
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.TRAIN)
] ]

View File

@ -147,15 +147,28 @@ class _AdminTestsMixin(object):
) )
self.assertEqual(1, len(r.json['trusts'])) self.assertEqual(1, len(r.json['trusts']))
def test_admin_cannot_get_trust_for_other_user(self):
PROVIDERS.trust_api.create_trust( class AdminTokenTests(TrustTests, _AdminTestsMixin):
"""Tests for the is_admin user.
The Trusts API has hardcoded is_admin checks that we need to ensure are
preserved through the system-scope transition.
"""
def setUp(self):
super(AdminTokenTests, self).setUp()
self.config_fixture.config(admin_token='ADMIN')
self.headers = {'X-Auth-Token': 'ADMIN'}
def test_admin_can_delete_trust_for_other_user(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data) self.trust_id, **self.trust_data)
with self.test_client() as c: with self.test_client() as c:
c.get( c.delete(
'/v3/OS-TRUST/trusts/%s' % self.trust_id, '/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.headers, headers=self.headers,
expected_status_code=http_client.FORBIDDEN expected_status_code=http_client.NO_CONTENT
) )
def test_admin_can_get_non_existent_trust_not_found(self): def test_admin_can_get_non_existent_trust_not_found(self):
@ -167,6 +180,17 @@ class _AdminTestsMixin(object):
expected_status_code=http_client.NOT_FOUND expected_status_code=http_client.NOT_FOUND
) )
def test_admin_cannot_get_trust_for_other_user(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % self.trust_id,
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
def test_admin_cannot_list_trust_roles_for_other_user(self): def test_admin_cannot_list_trust_roles_for_other_user(self):
PROVIDERS.trust_api.create_trust( PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data) self.trust_id, **self.trust_data)
@ -191,19 +215,118 @@ class _AdminTestsMixin(object):
) )
class AdminTokenTests(TrustTests, _AdminTestsMixin): class _SystemUserTests(object):
"""Tests for the is_admin user. """Tests for system admin, member, and reader."""
The Trusts API has hardcoded is_admin checks that we need to ensure are def test_user_can_get_non_existent_trust(self):
preserved through the system-scope transition. trust_id = uuid.uuid4().hex
""" with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % trust_id,
headers=self.headers,
expected_status_code=http_client.NOT_FOUND
)
def test_user_can_get_trust_for_other_user(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts/%s' % self.trust_id,
headers=self.headers
)
self.assertEqual(r.json['trust']['id'], self.trust_id)
def test_user_can_list_trusts_for_trustee(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustee_user_id=%s' %
self.trustee_user_id),
headers=self.headers
)
def test_user_can_list_trusts_for_trustor(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustor_user_id=%s' %
self.trustor_user_id),
headers=self.headers
)
def test_user_can_list_trust_roles_for_other_user(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts/%s/roles' % self.trust_id,
headers=self.headers
)
self.assertEqual(r.json['roles'][0]['id'],
self.bootstrapper.member_role_id)
def test_user_can_get_trust_role_for_other_user(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts/%s/roles/%s' %
(self.trust_id, self.bootstrapper.member_role_id)),
headers=self.headers
)
class SystemReaderTests(TrustTests, _SystemUserTests):
"""Tests for system reader users."""
def setUp(self): def setUp(self):
super(AdminTokenTests, self).setUp() super(SystemReaderTests, self).setUp()
self.config_fixture.config(admin_token='ADMIN') self.config_fixture.config(group='oslo_policy', enforce_scope=True)
self.headers = {'X-Auth-Token': 'ADMIN'}
def test_admin_can_delete_trust_for_other_user(self): system_reader = unit.new_user_ref(
domain_id=CONF.identity.default_domain_id
)
self.user_id = PROVIDERS.identity_api.create_user(
system_reader
)['id']
PROVIDERS.assignment_api.create_system_grant_for_user(
self.user_id, self.bootstrapper.reader_role_id
)
auth = self.build_authentication_request(
user_id=self.user_id,
password=system_reader['password'],
system=True
)
# Grab a token using the persona we're testing and prepare headers
# for requests we'll be making in the tests.
with self.test_client() as c:
r = c.post('/v3/auth/tokens', json=auth)
self.token_id = r.headers['X-Subject-Token']
self.headers = {'X-Auth-Token': self.token_id}
def test_user_cannot_create_trust(self):
json = {'trust': self.trust_data['trust']}
json['trust']['roles'] = self.trust_data['roles']
with self.test_client() as c:
c.post(
'/v3/OS-TRUST/trusts',
json=json,
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_delete_trust(self):
ref = PROVIDERS.trust_api.create_trust( ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data) self.trust_id, **self.trust_data)
@ -211,11 +334,11 @@ class AdminTokenTests(TrustTests, _AdminTestsMixin):
c.delete( c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'], '/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.headers, headers=self.headers,
expected_status_code=http_client.NO_CONTENT expected_status_code=http_client.FORBIDDEN
) )
class SystemAdminTests(TrustTests, _AdminTestsMixin): class SystemAdminTests(TrustTests, _AdminTestsMixin, _SystemUserTests):
"""Tests for system admin users.""" """Tests for system admin users."""
def setUp(self): def setUp(self):
@ -250,18 +373,6 @@ class SystemAdminTests(TrustTests, _AdminTestsMixin):
expected_status_code=http_client.FORBIDDEN expected_status_code=http_client.FORBIDDEN
) )
def test_admin_list_all_trusts_overridden_defaults(self):
self._override_policy_old_defaults()
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts',
headers=self.headers
)
self.assertEqual(1, len(r.json['trusts']))
def test_admin_cannot_delete_trust_for_user_overridden_defaults(self): def test_admin_cannot_delete_trust_for_user_overridden_defaults(self):
# only the is_admin admin can do this # only the is_admin admin can do this
self._override_policy_old_defaults() self._override_policy_old_defaults()
@ -312,6 +423,18 @@ class SystemAdminTests(TrustTests, _AdminTestsMixin):
expected_status_code=http_client.FORBIDDEN expected_status_code=http_client.FORBIDDEN
) )
def test_user_list_all_trusts_overridden_defaults(self):
self._override_policy_old_defaults()
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts',
headers=self.headers
)
self.assertEqual(1, len(r.json['trusts']))
class ProjectUserTests(TrustTests): class ProjectUserTests(TrustTests):
"""Tests for all project users.""" """Tests for all project users."""