From b699a53867d30fb5f23d6153c97542ab83e3f554 Mon Sep 17 00:00:00 2001 From: daniel-a-nguyen Date: Fri, 26 Jun 2015 19:08:02 -0700 Subject: [PATCH] Makes policy.check aware of domain scoped token This allows Domain Admin to also have a role on a Project(s). Supersedes: 196068 Partially Implements: blueprint domain-scoped-tokens Change-Id: Ia81e15a465117acf173c6459cec92838c825c460 --- openstack_auth/policy.py | 85 ++++++-- .../tests/conf/policy.v3cloudsample.json | 195 ++++++++++++++++++ openstack_auth/tests/tests.py | 40 ++++ 3 files changed, 302 insertions(+), 18 deletions(-) create mode 100644 openstack_auth/tests/conf/policy.v3cloudsample.json diff --git a/openstack_auth/policy.py b/openstack_auth/policy.py index 55b463f6..5461b22d 100644 --- a/openstack_auth/policy.py +++ b/openstack_auth/policy.py @@ -21,6 +21,7 @@ from oslo_config import cfg from oslo_policy import opts as policy_opts from oslo_policy import policy +from openstack_auth import user as auth_user from openstack_auth import utils as auth_utils LOG = logging.getLogger(__name__) @@ -123,37 +124,63 @@ def check(actions, request, target=None): # same for user_id if target.get('user_id') is None: target['user_id'] = user.id - # same for domain_id - if target.get('domain_id') is None: - target['domain_id'] = user.domain_id - credentials = _user_to_credentials(request, user) + domain_id_keys = [ + 'domain_id', + 'project.domain_id', + 'user.domain_id', + 'group.domain_id' + ] + # populates domain id keys with user's current domain id + for key in domain_id_keys: + if target.get(key) is None: + target[key] = user.user_domain_id + + credentials = _user_to_credentials(user) + domain_credentials = _domain_to_credentials(request, user) + # if there is a domain token use the domain_id instead of the user's domain + if domain_credentials: + credentials['domain_id'] = domain_credentials.get('domain_id') enforcer = _get_enforcer() for action in actions: scope, action = action[0], action[1] if scope in enforcer: - # if any check fails return failure - if not enforcer[scope].enforce(action, target, credentials): - # to match service implementations, if a rule is not found, - # use the default rule for that service policy - # - # waiting to make the check because the first call to - # enforce loads the rules - if action not in enforcer[scope].rules: - if not enforcer[scope].enforce('default', - target, credentials): - return False - else: - return False + # this is for handling the v3 policy file and will only be + # needed when a domain scoped token is present + if scope == 'identity' and domain_credentials: + # use domain credentials + return _check_credentials( + enforcer[scope], action, target, domain_credentials) + + # use project credentials + return _check_credentials( + enforcer[scope], action, target, credentials) + # if no policy for scope, allow action, underlying API will # ultimately block the action if not permitted, treat as though # allowed return True -def _user_to_credentials(request, user): +def _check_credentials(enforcer_scope, action, target, credentials): + is_valid = True + if not enforcer_scope.enforce(action, target, credentials): + # to match service implementations, if a rule is not found, + # use the default rule for that service policy + # + # waiting to make the check because the first call to + # enforce loads the rules + if action not in enforcer_scope.rules: + if not enforcer_scope.enforce('default', target, credentials): + is_valid = False + else: + is_valid = False + return is_valid + + +def _user_to_credentials(user): if not hasattr(user, "_credentials"): roles = [role['name'] for role in user.roles] user._credentials = {'user_id': user.id, @@ -165,3 +192,25 @@ def _user_to_credentials(request, user): 'is_admin': user.is_superuser, 'roles': roles} return user._credentials + + +def _domain_to_credentials(request, user): + if not hasattr(user, "_domain_credentials"): + try: + domain_auth_ref = request.session.get('domain_token') + + # no domain role or not running on V3 + if not domain_auth_ref: + return None + domain_user = auth_user.create_user_from_token( + request, auth_user.Token(domain_auth_ref), + domain_auth_ref.service_catalog.url_for(endpoint_type=None)) + user._domain_credentials = _user_to_credentials(domain_user) + + # uses the domain_id associated with the domain_user + user._domain_credentials['domain_id'] = domain_user.domain_id + + except Exception: + LOG.error("Failed to create user from domain scoped token.") + return None + return user._domain_credentials diff --git a/openstack_auth/tests/conf/policy.v3cloudsample.json b/openstack_auth/tests/conf/policy.v3cloudsample.json new file mode 100644 index 00000000..a96996c6 --- /dev/null +++ b/openstack_auth/tests/conf/policy.v3cloudsample.json @@ -0,0 +1,195 @@ +{ + "admin_required": "role:admin", + "cloud_admin": "rule:admin_required and domain_id:admin_domain_id", + "service_role": "role:service", + "service_or_admin": "rule:admin_required or rule:service_role", + "owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s", + "admin_or_owner": "(rule:admin_required and domain_id:%(target.token.user.domain.id)s) or rule:owner", + "admin_or_cloud_admin": "rule:admin_required or rule:cloud_admin", + "admin_and_matching_domain_id": "rule:admin_required and domain_id:%(domain_id)s", + "service_admin_or_owner": "rule:service_or_admin or rule:owner", + + "default": "rule:admin_required", + + "identity:get_region": "", + "identity:list_regions": "", + "identity:create_region": "rule:cloud_admin", + "identity:update_region": "rule:cloud_admin", + "identity:delete_region": "rule:cloud_admin", + + "identity:get_service": "rule:admin_or_cloud_admin", + "identity:list_services": "rule:admin_or_cloud_admin", + "identity:create_service": "rule:cloud_admin", + "identity:update_service": "rule:cloud_admin", + "identity:delete_service": "rule:cloud_admin", + + "identity:get_endpoint": "rule:admin_or_cloud_admin", + "identity:list_endpoints": "rule:admin_or_cloud_admin", + "identity:create_endpoint": "rule:cloud_admin", + "identity:update_endpoint": "rule:cloud_admin", + "identity:delete_endpoint": "rule:cloud_admin", + + "identity:get_domain": "rule:cloud_admin or rule:admin_and_matching_domain_id", + "identity:list_domains": "rule:cloud_admin", + "identity:create_domain": "rule:cloud_admin", + "identity:update_domain": "rule:cloud_admin", + "identity:delete_domain": "rule:cloud_admin", + + "admin_and_matching_target_project_domain_id": "rule:admin_required and domain_id:%(target.project.domain_id)s", + "admin_and_matching_project_domain_id": "rule:admin_required and domain_id:%(project.domain_id)s", + "identity:get_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", + "identity:list_projects": "rule:cloud_admin or rule:admin_and_matching_domain_id", + "identity:list_user_projects": "rule:owner or rule:admin_and_matching_domain_id", + "identity:create_project": "rule:cloud_admin or rule:admin_and_matching_project_domain_id", + "identity:update_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", + "identity:delete_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id", + + "admin_and_matching_target_user_domain_id": "rule:admin_required and domain_id:%(target.user.domain_id)s", + "admin_and_matching_user_domain_id": "rule:admin_required and domain_id:%(user.domain_id)s", + "identity:get_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", + "identity:list_users": "rule:cloud_admin or rule:admin_and_matching_domain_id", + "identity:create_user": "rule:cloud_admin or rule:admin_and_matching_user_domain_id", + "identity:update_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", + "identity:delete_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id", + + "admin_and_matching_target_group_domain_id": "rule:admin_required and domain_id:%(target.group.domain_id)s", + "admin_and_matching_group_domain_id": "rule:admin_required and domain_id:%(group.domain_id)s", + "identity:get_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:list_groups": "rule:cloud_admin or rule:admin_and_matching_domain_id", + "identity:list_groups_for_user": "rule:owner or rule:admin_and_matching_domain_id", + "identity:create_group": "rule:cloud_admin or rule:admin_and_matching_group_domain_id", + "identity:update_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:delete_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:list_users_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:remove_user_from_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:check_user_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + "identity:add_user_to_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id", + + "identity:get_credential": "rule:admin_required", + "identity:list_credentials": "rule:admin_required or user_id:%(user_id)s", + "identity:create_credential": "rule:admin_required", + "identity:update_credential": "rule:admin_required", + "identity:delete_credential": "rule:admin_required", + + "identity:ec2_get_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)", + "identity:ec2_list_credentials": "rule:admin_or_cloud_admin or rule:owner", + "identity:ec2_create_credential": "rule:admin_or_cloud_admin or rule:owner", + "identity:ec2_delete_credential": "rule:admin_or_cloud_admin or (rule:owner and user_id:%(target.credential.user_id)s)", + + "identity:get_role": "rule:admin_or_cloud_admin", + "identity:list_roles": "rule:admin_or_cloud_admin", + "identity:create_role": "rule:cloud_admin", + "identity:update_role": "rule:cloud_admin", + "identity:delete_role": "rule:cloud_admin", + + "domain_admin_for_grants": "rule:admin_required and (domain_id:%(domain_id)s or domain_id:%(target.project.domain_id)s)", + "project_admin_for_grants": "rule:admin_required and project_id:%(project_id)s", + "identity:check_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", + "identity:list_grants": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", + "identity:create_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", + "identity:revoke_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants", + + "admin_on_domain_filter" : "rule:admin_required and domain_id:%(scope.domain.id)s", + "admin_on_project_filter" : "rule:admin_required and project_id:%(scope.project.id)s", + "identity:list_role_assignments": "rule:cloud_admin or rule:admin_on_domain_filter or rule:admin_on_project_filter", + + "identity:get_policy": "rule:cloud_admin", + "identity:list_policies": "rule:cloud_admin", + "identity:create_policy": "rule:cloud_admin", + "identity:update_policy": "rule:cloud_admin", + "identity:delete_policy": "rule:cloud_admin", + + "identity:change_password": "rule:owner", + "identity:check_token": "rule:admin_or_owner", + "identity:validate_token": "rule:service_admin_or_owner", + "identity:validate_token_head": "rule:service_or_admin", + "identity:revocation_list": "rule:service_or_admin", + "identity:revoke_token": "rule:admin_or_owner", + + "identity:create_trust": "user_id:%(trust.trustor_user_id)s", + "identity:list_trusts": "", + "identity:list_roles_for_trust": "", + "identity:get_role_for_trust": "", + "identity:delete_trust": "", + + "identity:create_consumer": "rule:admin_required", + "identity:get_consumer": "rule:admin_required", + "identity:list_consumers": "rule:admin_required", + "identity:delete_consumer": "rule:admin_required", + "identity:update_consumer": "rule:admin_required", + + "identity:authorize_request_token": "rule:admin_required", + "identity:list_access_token_roles": "rule:admin_required", + "identity:get_access_token_role": "rule:admin_required", + "identity:list_access_tokens": "rule:admin_required", + "identity:get_access_token": "rule:admin_required", + "identity:delete_access_token": "rule:admin_required", + + "identity:list_projects_for_endpoint": "rule:admin_required", + "identity:add_endpoint_to_project": "rule:admin_required", + "identity:check_endpoint_in_project": "rule:admin_required", + "identity:list_endpoints_for_project": "rule:admin_required", + "identity:remove_endpoint_from_project": "rule:admin_required", + + "identity:create_endpoint_group": "rule:admin_required", + "identity:list_endpoint_groups": "rule:admin_required", + "identity:get_endpoint_group": "rule:admin_required", + "identity:update_endpoint_group": "rule:admin_required", + "identity:delete_endpoint_group": "rule:admin_required", + "identity:list_projects_associated_with_endpoint_group": "rule:admin_required", + "identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required", + "identity:get_endpoint_group_in_project": "rule:admin_required", + "identity:list_endpoint_groups_for_project": "rule:admin_required", + "identity:add_endpoint_group_to_project": "rule:admin_required", + "identity:remove_endpoint_group_from_project": "rule:admin_required", + + "identity:create_identity_provider": "rule:cloud_admin", + "identity:list_identity_providers": "rule:cloud_admin", + "identity:get_identity_providers": "rule:cloud_admin", + "identity:update_identity_provider": "rule:cloud_admin", + "identity:delete_identity_provider": "rule:cloud_admin", + + "identity:create_protocol": "rule:cloud_admin", + "identity:update_protocol": "rule:cloud_admin", + "identity:get_protocol": "rule:cloud_admin", + "identity:list_protocols": "rule:cloud_admin", + "identity:delete_protocol": "rule:cloud_admin", + + "identity:create_mapping": "rule:cloud_admin", + "identity:get_mapping": "rule:cloud_admin", + "identity:list_mappings": "rule:cloud_admin", + "identity:delete_mapping": "rule:cloud_admin", + "identity:update_mapping": "rule:cloud_admin", + + "identity:create_service_provider": "rule:cloud_admin", + "identity:list_service_providers": "rule:cloud_admin", + "identity:get_service_provider": "rule:cloud_admin", + "identity:update_service_provider": "rule:cloud_admin", + "identity:delete_service_provider": "rule:cloud_admin", + + "identity:get_auth_catalog": "", + "identity:get_auth_projects": "", + "identity:get_auth_domains": "", + + "identity:list_projects_for_groups": "", + "identity:list_domains_for_groups": "", + + "identity:list_revoke_events": "", + + "identity:create_policy_association_for_endpoint": "rule:cloud_admin", + "identity:check_policy_association_for_endpoint": "rule:cloud_admin", + "identity:delete_policy_association_for_endpoint": "rule:cloud_admin", + "identity:create_policy_association_for_service": "rule:cloud_admin", + "identity:check_policy_association_for_service": "rule:cloud_admin", + "identity:delete_policy_association_for_service": "rule:cloud_admin", + "identity:create_policy_association_for_region_and_service": "rule:cloud_admin", + "identity:check_policy_association_for_region_and_service": "rule:cloud_admin", + "identity:delete_policy_association_for_region_and_service": "rule:cloud_admin", + "identity:get_policy_for_endpoint": "rule:cloud_admin", + "identity:list_endpoints_for_policy": "rule:cloud_admin", + + "identity:create_domain_config": "rule:cloud_admin", + "identity:get_domain_config": "rule:cloud_admin", + "identity:update_domain_config": "rule:cloud_admin", + "identity:delete_domain_config": "rule:cloud_admin" +} diff --git a/openstack_auth/tests/tests.py b/openstack_auth/tests/tests.py index 95928f88..11d5511e 100644 --- a/openstack_auth/tests/tests.py +++ b/openstack_auth/tests/tests.py @@ -1094,3 +1094,43 @@ class PolicyTestCaseAdmin(PolicyTestCase): value = policy.check((("compute", "context_is_admin"),), request=self.request) self.assertTrue(value) + + +class PolicyTestCaseV3Admin(PolicyTestCase): + _roles = [{'id': '1', 'name': 'admin'}] + + def setUp(self): + policy_files = { + 'identity': 'policy.v3cloudsample.json', + 'compute': 'nova_policy.json'} + + override = self.settings(POLICY_FILES=policy_files) + override.enable() + self.addCleanup(override.disable) + + mock_user = user.User(id=1, roles=self._roles, + user_domain_id='admin_domain_id') + patcher = mock.patch('openstack_auth.utils.get_user', + return_value=mock_user) + self.MockClass = patcher.start() + self.addCleanup(patcher.stop) + self.request = http.HttpRequest() + + def test_check_cloud_admin_required_true(self): + policy.reset() + value = policy.check((("identity", "cloud_admin"),), + request=self.request) + self.assertTrue(value) + + def test_check_domain_admin_required_true(self): + policy.reset() + value = policy.check(( + ("identity", "admin_and_matching_domain_id"),), + request=self.request) + self.assertTrue(value) + + def test_check_any_admin_required_true(self): + policy.reset() + value = policy.check((("identity", "admin_or_cloud_admin"),), + request=self.request) + self.assertTrue(value)