From ef838a3a3f575562b1fc84623c3a8491d4f2e2f4 Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Fri, 22 Mar 2019 21:25:07 +0000 Subject: [PATCH] Make system admin policies consistent for grants This commit adjust the create and revoke grant policies to be consistent with other system admin policy check strings by not using the rule:admin_required check string and by including system_scope:all in the rule itself. Subsequent patches will: - implement domain reader and member support - implement domain admin support - introduce test coverage for project users and the grants API - remove redundant policies from policy.v3cloudsample.json Related-Bug: 1805368 Related-Bug: 1750669 Related-Bug: 1806762 Change-Id: Idcbe16f643332d80af716074cf3ea22525d465a9 --- keystone/common/policies/grant.py | 20 +- .../tests/unit/protection/v3/test_grants.py | 193 ++++++++++++++++++ .../notes/bug-1750669-dfce859550126f03.yaml | 42 ++-- 3 files changed, 235 insertions(+), 20 deletions(-) diff --git a/keystone/common/policies/grant.py b/keystone/common/policies/grant.py index 57a8593e64..5c383b0ca3 100644 --- a/keystone/common/policies/grant.py +++ b/keystone/common/policies/grant.py @@ -53,6 +53,12 @@ deprecated_list_grants = policy.DeprecatedRule( deprecated_check_grant = policy.DeprecatedRule( name=base.IDENTITY % 'check_grant', check_str=base.RULE_ADMIN_REQUIRED ) +deprecated_create_grant = policy.DeprecatedRule( + name=base.IDENTITY % 'create_grant', check_str=base.RULE_ADMIN_REQUIRED +) +deprecated_revoke_grant = policy.DeprecatedRule( + name=base.IDENTITY % 'revoke_grant', check_str=base.RULE_ADMIN_REQUIRED +) DEPRECATED_REASON = """ As of the Stein release, the assignment API now understands default roles and @@ -141,7 +147,7 @@ grant_policies = [ deprecated_since=versionutils.deprecated.STEIN), policy.DocumentedRuleDefault( name=base.IDENTITY % 'create_grant', - check_str=base.RULE_ADMIN_REQUIRED, + check_str=base.SYSTEM_ADMIN, # FIXME(lbragstad): See the above comment about scope_types before # adding 'project' to scope_types below. scope_types=['system'], @@ -151,10 +157,13 @@ grant_policies = [ 'to the OS-INHERIT APIs, where grants on the target ' 'are inherited to all projects in the subtree, if ' 'applicable.'), - operations=list_operations(resource_paths, ['PUT'])), + operations=list_operations(resource_paths, ['PUT']), + deprecated_rule=deprecated_create_grant, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.STEIN), policy.DocumentedRuleDefault( name=base.IDENTITY % 'revoke_grant', - check_str=base.RULE_ADMIN_REQUIRED, + check_str=base.SYSTEM_ADMIN, # FIXME(lbragstad): See the above comment about scope_types before # adding 'project' to scope_types below. scope_types=['system'], @@ -166,7 +175,10 @@ grant_policies = [ 'applicable. In that case, revoking the role grant in ' 'the target would remove the logical effect of ' 'inheriting it to the target\'s projects subtree.'), - operations=list_operations(resource_paths, ['DELETE'])), + operations=list_operations(resource_paths, ['DELETE']), + deprecated_rule=deprecated_revoke_grant, + deprecated_reason=DEPRECATED_REASON, + deprecated_since=versionutils.deprecated.STEIN), policy.DocumentedRuleDefault( name=base.IDENTITY % 'list_system_grants_for_user', check_str=base.SYSTEM_READER, diff --git a/keystone/tests/unit/protection/v3/test_grants.py b/keystone/tests/unit/protection/v3/test_grants.py index 9f9be8bf45..1628cd6ada 100644 --- a/keystone/tests/unit/protection/v3/test_grants.py +++ b/keystone/tests/unit/protection/v3/test_grants.py @@ -460,3 +460,196 @@ class SystemMemberTests(base_classes.TestCaseWithBootstrap, r = c.post('/v3/auth/tokens', json=auth) self.token_id = r.headers['X-Subject-Token'] self.headers = {'X-Auth-Token': self.token_id} + + +class SystemAdminTests(base_classes.TestCaseWithBootstrap, + common_auth.AuthTestMixin, + _SystemUserGrantTests): + + def setUp(self): + super(SystemAdminTests, self).setUp() + self.loadapp() + self.useFixture(ksfixtures.Policy(self.config_fixture)) + self.config_fixture.config(group='oslo_policy', enforce_scope=True) + + self.user_id = self.bootstrapper.admin_user_id + auth = self.build_authentication_request( + user_id=self.user_id, + password=self.bootstrapper.admin_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_can_create_grant_for_user_on_project(self): + user = PROVIDERS.identity_api.create_user( + unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + ) + + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, unit.new_project_ref( + domain_id=CONF.identity.default_domain_id + ) + ) + + with self.test_client() as c: + c.put( + '/v3/projects/%s/users/%s/roles/%s' % ( + project['id'], user['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_create_grant_for_user_on_domain(self): + user = PROVIDERS.identity_api.create_user( + unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + ) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + with self.test_client() as c: + c.put( + '/v3/domains/%s/users/%s/roles/%s' % ( + domain['id'], user['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_create_grant_for_group_on_project(self): + group = PROVIDERS.identity_api.create_group( + unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + ) + + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, unit.new_project_ref( + domain_id=CONF.identity.default_domain_id + ) + ) + + with self.test_client() as c: + c.put( + '/v3/projects/%s/groups/%s/roles/%s' % ( + project['id'], + group['id'], + self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_create_grant_for_group_on_domain(self): + group = PROVIDERS.identity_api.create_group( + unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + ) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + with self.test_client() as c: + c.put( + '/v3/domains/%s/groups/%s/roles/%s' % ( + domain['id'], group['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_revoke_grant_from_user_on_project(self): + user = PROVIDERS.identity_api.create_user( + unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + ) + + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, unit.new_project_ref( + domain_id=CONF.identity.default_domain_id + ) + ) + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + project_id=project['id'] + ) + + with self.test_client() as c: + c.delete( + '/v3/projects/%s/users/%s/roles/%s' % ( + project['id'], user['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_revoke_grant_from_user_on_domain(self): + user = PROVIDERS.identity_api.create_user( + unit.new_user_ref(domain_id=CONF.identity.default_domain_id) + ) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, user_id=user['id'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + c.delete( + '/v3/domains/%s/users/%s/roles/%s' % ( + domain['id'], user['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_revoke_grant_from_group_on_project(self): + group = PROVIDERS.identity_api.create_group( + unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + ) + + project = PROVIDERS.resource_api.create_project( + uuid.uuid4().hex, unit.new_project_ref( + domain_id=CONF.identity.default_domain_id + ) + ) + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, group_id=group['id'], + project_id=project['id'] + ) + + with self.test_client() as c: + c.delete( + '/v3/projects/%s/groups/%s/roles/%s' % ( + project['id'], + group['id'], + self.bootstrapper.reader_role_id + ), + headers=self.headers + ) + + def test_user_can_revoke_grant_from_group_on_domain(self): + group = PROVIDERS.identity_api.create_group( + unit.new_group_ref(domain_id=CONF.identity.default_domain_id) + ) + + domain = PROVIDERS.resource_api.create_domain( + uuid.uuid4().hex, unit.new_domain_ref() + ) + + PROVIDERS.assignment_api.create_grant( + self.bootstrapper.reader_role_id, group_id=group['id'], + domain_id=domain['id'] + ) + + with self.test_client() as c: + c.delete( + '/v3/domains/%s/groups/%s/roles/%s' % ( + domain['id'], group['id'], self.bootstrapper.reader_role_id + ), + headers=self.headers + ) diff --git a/releasenotes/notes/bug-1750669-dfce859550126f03.yaml b/releasenotes/notes/bug-1750669-dfce859550126f03.yaml index 6405f86f3d..8415963c46 100644 --- a/releasenotes/notes/bug-1750669-dfce859550126f03.yaml +++ b/releasenotes/notes/bug-1750669-dfce859550126f03.yaml @@ -4,39 +4,49 @@ features: [`bug 1805368 `_] [`bug 1750669 `_] The system assignment API now supports the ``admin``, ``member``, - and ``reader`` default roles across system-scope, domain-scope, - and project-scope. + and ``reader`` default roles across system-scope, domain-scope, and + project-scope. The grant API now supports the ``admin``, + ``member``, and ``reader`` default roles for system-scope. upgrade: - | [`bug 1805368 `_] [`bug 1750669 `_] - The system assignment API uses new default policies that make it more - accessible to end users and administrators in a secure way. Please - consider these new defaults if your deployment overrides system - assignment policies. + The system assignment and grant APIs uses new default policies that + make it more accessible to end users and administrators in a secure + way. Please consider these new defaults if your deployment + overrides system assignment policies. deprecations: - | [`bug 1805368 `_] [`bug 1750669 `_] - The system assignment policies have been deprecated. The + The system assignment and grant policies have been deprecated. The ``identity:list_system_grants_for_user``, ``identity:check_system_grant_for_user``, ``identity:list_system_grants_for_group``, and ``identity:check_system_grant_for_group`` policies now use ``role:reader and system_scope:all`` instead of - ``rule:admin_required``. The ``identity:create_system_grant_for_user``, + ``rule:admin_required``. The + ``identity:create_system_grant_for_user``, ``identity:revoke_system_grant_for_user``, ``identity:create_system_grant_for_group``, and - ``identity:revoke_system_grant_for_group`` policies now use ``role:admin - and system_scope:all`` instead of ``rule:admin_required``. These new - defaults automatically include support for a read-only role and allow for - more granular access to the system assignment API, making it easier for - administrators to delegate authorization, safely. Please consider these new - defaults if your deployment overrides the system assignment APIs. + ``identity:revoke_system_grant_for_group`` policies now use + ``role:admin and system_scope:all`` instead of + ``rule:admin_required``. The ``identity:check_grant`` and + ``identity:list_grants`` policies now use ``role:reader and + system_scope:all`` instead of ``rule:admin_required``. The + ``identity:create_grant`` and ``identity:revoke_grant`` policies + now use ``role:admin and system_scope:all`` instead of + ``rule:admin_required``. These new defaults automatically include + support for a read-only role and allow for more granular access to + the system assignment and grant APIs, making it easier for + administrators to delegate authorization, safely. Please consider + these new defaults if your deployment overrides the system + assignment APIs. security: - | [`bug 1805368 `_] [`bug 1750669 `_] The system assignment API now uses system-scope, domain-scope, - project-scope, and default roles to provide better accessibility - to users in a secure way. + project-scope, and default roles to provide better accessibility to + users in a secure way. The grant API now uses system-scope and + default to provide better accessbility to operators.