Browse Source

Allow project users to retrieve domains

This commit adds thorough testing to make sure users who have a role
on a project can use project-scoped tokens to call GET
/v3/domain/{domain_id} for the domain own their project. These users
are not allowed to access domains that they don't have any
authorization via project role assignments.

This ensures the domains API is tested with these cases and makes the
domains API more self-serviceable for users that are not
administrators.

Change-Id: Ifc100a7a235140fbd07cbafe80983d3c2f17a7dc
Closes-Bug: 1794864
Related-Bug: 968696
changes/71/605871/10
Lance Bragstad 3 years ago
parent
commit
2c8f81af62
  1. 12
      keystone/common/policies/domain.py
  2. 156
      keystone/tests/unit/protection/v3/test_domains.py
  3. 41
      releasenotes/notes/bug-1794864-3116bf165a146be6.yaml

12
keystone/common/policies/domain.py

@ -43,17 +43,19 @@ deprecated_delete_domain = policy.DeprecatedRule(
name=base.IDENTITY % 'delete_domain',
check_str=base.RULE_ADMIN_REQUIRED
)
SYSTEM_USER_OR_DOMAIN_USER_OR_PROJECT_USER = (
'(role:reader and system_scope:all) or '
'token.domain.id:%(target.domain.id)s or '
'token.project.domain.id:%(target.domain.id)s'
)
domain_policies = [
policy.DocumentedRuleDefault(
name=base.IDENTITY % 'get_domain',
# NOTE(lbragstad): This policy allows system, domain, and
# project-scoped tokens.
check_str=(
'(role:reader and system_scope:all) or '
'token.domain.id:%(target.domain.id)s or '
'token.project.domain.id:%(target.domain.id)s'
),
check_str=SYSTEM_USER_OR_DOMAIN_USER_OR_PROJECT_USER,
scope_types=['system', 'domain', 'project'],
description='Show domain details.',
operations=[{'path': '/v3/domains/{domain_id}',

156
keystone/tests/unit/protection/v3/test_domains.py

@ -12,14 +12,17 @@
import uuid
from oslo_serialization import jsonutils
from six.moves import http_client
from keystone.common.policies import domain as dp
from keystone.common import provider_api
import keystone.conf
from keystone.tests.common import auth as common_auth
from keystone.tests import unit
from keystone.tests.unit import base_classes
from keystone.tests.unit import ksfixtures
from keystone.tests.unit.ksfixtures import temporaryfile
CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs
@ -384,3 +387,156 @@ class DomainUserTests(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 ProjectReaderTests(base_classes.TestCaseWithBootstrap,
common_auth.AuthTestMixin,
_DomainAndProjectUserDomainTests):
def setUp(self):
super(ProjectReaderTests, self).setUp()
self.loadapp()
self.useFixture(ksfixtures.Policy(self.config_fixture))
self.config_fixture.config(group='oslo_policy', enforce_scope=True)
domain = PROVIDERS.resource_api.create_domain(
uuid.uuid4().hex, unit.new_domain_ref()
)
self.domain_id = domain['id']
project_reader = unit.new_user_ref(domain_id=self.domain_id)
project_reader_id = PROVIDERS.identity_api.create_user(
project_reader
)['id']
project = unit.new_project_ref(domain_id=self.domain_id)
project_id = PROVIDERS.resource_api.create_project(
project['id'], project
)['id']
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.reader_role_id, user_id=project_reader_id,
project_id=project_id
)
auth = self.build_authentication_request(
user_id=project_reader_id,
password=project_reader['password'],
project_id=project_id
)
# 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}
class ProjectMemberTests(base_classes.TestCaseWithBootstrap,
common_auth.AuthTestMixin,
_DomainAndProjectUserDomainTests):
def setUp(self):
super(ProjectMemberTests, self).setUp()
self.loadapp()
self.useFixture(ksfixtures.Policy(self.config_fixture))
self.config_fixture.config(group='oslo_policy', enforce_scope=True)
domain = PROVIDERS.resource_api.create_domain(
uuid.uuid4().hex, unit.new_domain_ref()
)
self.domain_id = domain['id']
project_member = unit.new_user_ref(domain_id=self.domain_id)
project_member_id = PROVIDERS.identity_api.create_user(
project_member
)['id']
project = unit.new_project_ref(domain_id=self.domain_id)
project_id = PROVIDERS.resource_api.create_project(
project['id'], project
)['id']
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.member_role_id, user_id=project_member_id,
project_id=project_id
)
auth = self.build_authentication_request(
user_id=project_member_id,
password=project_member['password'],
project_id=project_id
)
# 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}
class ProjectAdminTests(base_classes.TestCaseWithBootstrap,
common_auth.AuthTestMixin,
_DomainAndProjectUserDomainTests):
def setUp(self):
super(ProjectAdminTests, self).setUp()
self.loadapp()
self.policy_file = self.useFixture(temporaryfile.SecureTempFile())
self.policy_file_name = self.policy_file.file_name
self.useFixture(
ksfixtures.Policy(
self.config_fixture, policy_file=self.policy_file_name
)
)
self._override_policy()
self.config_fixture.config(group='oslo_policy', enforce_scope=True)
domain = PROVIDERS.resource_api.create_domain(
uuid.uuid4().hex, unit.new_domain_ref()
)
self.domain_id = domain['id']
project_admin = unit.new_user_ref(domain_id=self.domain_id)
project_admin_id = PROVIDERS.identity_api.create_user(
project_admin
)['id']
project = unit.new_project_ref(domain_id=self.domain_id)
project_id = PROVIDERS.resource_api.create_project(
project['id'], project
)['id']
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.admin_role_id, user_id=project_admin_id,
project_id=project_id
)
auth = self.build_authentication_request(
user_id=project_admin_id,
password=project_admin['password'],
project_id=project_id
)
# 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 _override_policy(self):
# TODO(lbragstad): Remove this once the deprecated policies in
# keystone.common.policies.domains have been removed. This is only
# here to make sure we test the new policies instead of the deprecated
# ones. Oslo.policy will OR deprecated policies with new policies to
# maintain compatibility and give operators a chance to update
# permissions or update policies without breaking users. This will
# cause these specific tests to fail since we're trying to correct this
# broken behavior with better scope checking.
with open(self.policy_file_name, 'w') as f:
overridden_policies = {
'identity:get_domain': (
dp.SYSTEM_USER_OR_DOMAIN_USER_OR_PROJECT_USER
)
}
f.write(jsonutils.dumps(overridden_policies))

41
releasenotes/notes/bug-1794864-3116bf165a146be6.yaml

@ -0,0 +1,41 @@
---
upgrade:
- |
[`bug 1794864 <https://bugs.launchpad.net/keystone/+bug/1794864>`_]
[`bug 1794376 <https://bugs.launchpad.net/keystone/+bug/1794376>`_]
The default policies that protect the domains API have been deprecated in
favor of ones that are more secure and self-serviceable. If you're
maintaining custom policies, please make sure you resolve your domain
policies to work with the new default by adding the proper role
assignments, or continue maintaining custom overrides. The new defaults
allow for better protection of the domains API when giving the `admin` role
to users on domains and projects.
deprecations:
- |
[`bug 1794864 <https://bugs.launchpad.net/keystone/+bug/1794864>`_]
[`bug 1794376 <https://bugs.launchpad.net/keystone/+bug/1794376>`_]
The default policies that protect the domains API have been deprecated in
favor of ones that are more secure and self-serviceable. If you're
maintaining custom policies, please make sure you resolve your domain
policies to work with the new default by adding the proper role
assignments, or continue maintaining custom overrides. The new defaults
allow for better protection of the domains API when giving the `admin` role
to users on domains and projects.
security:
- |
[`bug 1794864 <https://bugs.launchpad.net/keystone/+bug/1794864>`_]
[`bug 1794376 <https://bugs.launchpad.net/keystone/+bug/1794376>`_]
The default policies that protect the domains API have been deprecated in
favor of ones that are more secure and self-serviceable.
fixes:
- |
[`bug 1794864 <https://bugs.launchpad.net/keystone/+bug/1794864>`_]
[`bug 1794376 <https://bugs.launchpad.net/keystone/+bug/1794376>`_]
The default policies that protect the domains API have been deprecated in
favor of ones that are more secure and self-serviceable. Users with roles
on domains and projects are now able to call the
``GET /v3/domains/{domain_id}`` API if they use a token scoped to that
domain or a token scoped to a project within that domain. System users are
allowed to access the domain APIs in the same way legacy `admin` users were
able to. This allows for better protection of the domain API when giving
the `admin` role to users on domains and projects.
Loading…
Cancel
Save