diff --git a/keystone/api/trusts.py b/keystone/api/trusts.py index 374b7351c3..3c40d8c67f 100644 --- a/keystone/api/trusts.py +++ b/keystone/api/trusts.py @@ -392,11 +392,45 @@ class RolesForTrustListResource(flask_restful.Resource): # URL additions and does not have a collection key/member_key, we use # the flask-restful Resource, not the keystone ResourceBase class RoleForTrustResource(flask_restful.Resource): + + @property + def oslo_context(self): + return flask.request.environ.get(context.REQUEST_CONTEXT_ENV, None) + def get(self, trust_id, role_id): """Get a role that has been assigned to a trust.""" - ENFORCER.enforce_call(action='identity:get_role_for_trust') + ENFORCER.enforce_call(action='identity:get_role_for_trust', + build_target=_build_trust_target_enforcement) + + if self.oslo_context.is_admin: + # policies are not loaded for the is_admin context, so need to + # block access here + raise exception.ForbiddenAction( + action=_('Requested user has no relation to this trust')) + trust = PROVIDERS.trust_api.get_trust(trust_id) - _trustor_trustee_only(trust) + + # NOTE(cmurphy) As of Train, the default policies enforce the + # identity:get_role_for_trust rule. However, in case the + # identity:get_role_for_trust rule has been locally overridden by the + # default that would have been produced by the sample config, we need + # to enforce it again and warn that the behavior is changing. + rules = policy._ENFORCER._enforcer.rules.get( + 'identity:get_role_for_trust') + # rule check_str is "" + if isinstance(rules, op_checks.TrueCheck): + LOG.warning( + "The policy check string for rule " + "\"identity:get_role_for_trust\" has been overridden to " + "\"always true\". In the next release, this will cause the " + "\"identity:get_role_for_trust\" action to be fully " + "permissive as hardcoded enforcement will be removed. To " + "correct this issue, either stop overriding the " + "\"identity:get_role_for_trust\" rule in config to accept the " + "defaults, or explicitly set a rule that is not empty." + ) + _trustor_trustee_only(trust) + if not any(role['id'] == role_id for role in trust['roles']): raise exception.RoleNotFound(role_id=role_id) diff --git a/keystone/cmd/status.py b/keystone/cmd/status.py index e8e32d44cc..d3b6ee09bc 100644 --- a/keystone/cmd/status.py +++ b/keystone/cmd/status.py @@ -39,6 +39,7 @@ class Checks(upgradecheck.UpgradeCommands): 'identity:delete_trust', 'identity:get_trust', 'identity:list_roles_for_trust' + 'identity:get_role_for_trust' ] failed_rules = [] for rule in rules: diff --git a/keystone/common/policies/trust.py b/keystone/common/policies/trust.py index 55d6515fe1..06fdf9db31 100644 --- a/keystone/common/policies/trust.py +++ b/keystone/common/policies/trust.py @@ -66,7 +66,7 @@ trust_policies = [ 'method': 'HEAD'}]), policy.DocumentedRuleDefault( name=base.IDENTITY % 'get_role_for_trust', - check_str='', + check_str=RULE_TRUSTOR + ' or ' + RULE_TRUSTEE, scope_types=['project'], description='Check if trust delegates a particular role.', operations=[{'path': '/v3/OS-TRUST/trusts/{trust_id}/roles/{role_id}', diff --git a/keystone/tests/unit/protection/v3/test_trusts.py b/keystone/tests/unit/protection/v3/test_trusts.py index 78528975b5..8a5768966c 100644 --- a/keystone/tests/unit/protection/v3/test_trusts.py +++ b/keystone/tests/unit/protection/v3/test_trusts.py @@ -112,6 +112,7 @@ class TrustTests(base_classes.TestCaseWithBootstrap, 'identity:delete_trust': '', 'identity:get_trust': '', 'identity:list_roles_for_trust': '', + 'identity:get_role_for_trust': '', } f.write(jsonutils.dumps(overridden_policies)) @@ -298,6 +299,19 @@ class SystemAdminTests(TrustTests, _AdminTestsMixin): expected_status_code=http_client.FORBIDDEN ) + def test_admin_cannot_get_trust_role_for_other_user_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: + c.get( + ('/v3/OS-TRUST/trusts/%s/roles/%s' % + (self.trust_id, self.bootstrapper.member_role_id)), + headers=self.headers, + expected_status_code=http_client.FORBIDDEN + ) + class ProjectUserTests(TrustTests): """Tests for all project users.""" @@ -733,3 +747,40 @@ class ProjectUserTests(TrustTests): headers=self.other_headers, expected_status_code=http_client.FORBIDDEN ) + + def test_trustor_can_get_trust_role_overridden_default(self): + self._override_policy_old_defaults() + PROVIDERS.trust_api.create_trust( + self.trust_id, **self.trust_data) + + with self.test_client() as c: + c.head( + ('/v3/OS-TRUST/trusts/%s/roles/%s' % + (self.trust_id, self.bootstrapper.member_role_id)), + headers=self.trustor_headers + ) + + def test_trustee_can_get_trust_role_overridden_default(self): + self._override_policy_old_defaults() + PROVIDERS.trust_api.create_trust( + self.trust_id, **self.trust_data) + + with self.test_client() as c: + c.head( + ('/v3/OS-TRUST/trusts/%s/roles/%s' % + (self.trust_id, self.bootstrapper.member_role_id)), + headers=self.trustee_headers + ) + + def test_user_cannot_get_trust_role_other_user_overridden_default(self): + self._override_policy_old_defaults() + PROVIDERS.trust_api.create_trust( + self.trust_id, **self.trust_data) + + with self.test_client() as c: + c.head( + ('/v3/OS-TRUST/trusts/%s/roles/%s' % + (self.trust_id, self.bootstrapper.member_role_id)), + headers=self.other_headers, + expected_status_code=http_client.FORBIDDEN + ) diff --git a/keystone/tests/unit/test_cli.py b/keystone/tests/unit/test_cli.py index 76012241b1..15d87bab77 100644 --- a/keystone/tests/unit/test_cli.py +++ b/keystone/tests/unit/test_cli.py @@ -1868,7 +1868,8 @@ class CliStatusTestCase(unit.SQLDriverOverrides, unit.TestCase): 'identity:list_trusts': '', 'identity:delete_trust': '', 'identity:get_trust': '', - 'identity:list_roles_for_trust': '' + 'identity:list_roles_for_trust': '', + 'identity:get_role_for_trust': '' } f.write(jsonutils.dumps(overridden_policies)) result = self.checks.check_trust_policies_are_not_empty() @@ -1878,7 +1879,8 @@ class CliStatusTestCase(unit.SQLDriverOverrides, unit.TestCase): 'identity:list_trusts': 'rule:admin_required', 'identity:delete_trust': 'rule:admin_required', 'identity:get_trust': 'rule:admin_required', - 'identity:list_roles_for_trust': 'rule:admin_required' + 'identity:list_roles_for_trust': 'rule:admin_required', + 'identity:get_role_for_trust': 'rule:admin_required' } f.write(jsonutils.dumps(overridden_policies)) result = self.checks.check_trust_policies_are_not_empty()