implement system scope for application credential
Implement system scopes, namely 'admin', 'reader', and 'member' for the application credential API. Thus, making it consistent with other system-scoped policy definitions. For the application credential API, the follow policies will be enforced: - system admin can fetch, list, lookup, and delete user's application credentials. - system member and reader can only fetch, list, and lookup user's application credentials. Deleting a user's application credential other their own is strictly prohibitted. - domain and project admins can no longer touch user's application credentials other their own. - domain and project readers cannot touch user's application credentials other their own. - domain and project members cannot touch user's application credentials other their own. - create an application credential can only be done by the owner. No one else can create an application credential on behalf of another user. Test cases are added to guard the above policy changes. Change-Id: I26ee11571b6d0f700a5fe3a62ad2e8fc7f5316fe Closes-Bug: 1818725 Closes-Bug: 1750615
This commit is contained in:
parent
e9ee189b43
commit
52da4d0e12
@ -10,6 +10,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import versionutils
|
||||
from oslo_policy import policy
|
||||
|
||||
from keystone.common.policies import base
|
||||
@ -17,10 +18,31 @@ from keystone.common.policies import base
|
||||
collection_path = '/v3/users/{user_id}/application_credentials'
|
||||
resource_path = collection_path + '/{application_credential_id}'
|
||||
|
||||
deprecated_list_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'list_application_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER
|
||||
)
|
||||
deprecated_get_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'get_application_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER
|
||||
)
|
||||
deprecated_delete_application_credentials_for_user = policy.DeprecatedRule(
|
||||
name=base.IDENTITY % 'delete_application_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER
|
||||
)
|
||||
|
||||
DEPRECATED_REASON = """
|
||||
As of the Train release, the application credential API understands how to
|
||||
handle system-scoped tokens in addition to project tokens, making the API
|
||||
more accessible to users without compromising security or manageability for
|
||||
administrators. The new default policies for this API account for these changes
|
||||
automatically.
|
||||
"""
|
||||
|
||||
application_credential_policies = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'get_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
check_str=base.RULE_SYSTEM_READER_OR_OWNER,
|
||||
# FIXME(cmurphy) A system administrator should be able to manage any
|
||||
# application credential. A user with a role on a project should be
|
||||
# able to manage their own application credential. We don't currently
|
||||
@ -29,35 +51,44 @@ application_credential_policies = [
|
||||
# project. scope_types will remain commented out for now and will be
|
||||
# updated when we have an answer for this. The same applies to the
|
||||
# other policies in this file.
|
||||
# scope_types=['system', 'project'],
|
||||
scope_types=['system', 'project'],
|
||||
description='Show application credential details.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'GET'},
|
||||
{'path': resource_path,
|
||||
'method': 'HEAD'}]),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_get_application_credentials_for_user,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'list_application_credentials',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
# scope_types=['system', 'project'],
|
||||
check_str=base.RULE_SYSTEM_READER_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='List application credentials for a user.',
|
||||
operations=[{'path': collection_path,
|
||||
'method': 'GET'},
|
||||
{'path': collection_path,
|
||||
'method': 'HEAD'}]),
|
||||
'method': 'HEAD'}],
|
||||
deprecated_rule=deprecated_list_application_credentials_for_user,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'create_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
# scope_types=['system', 'project'],
|
||||
check_str=base.RULE_OWNER,
|
||||
scope_types=['project'],
|
||||
description='Create an application credential.',
|
||||
operations=[{'path': collection_path,
|
||||
'method': 'POST'}]),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=base.IDENTITY % 'delete_application_credential',
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
# scope_types=['system', 'project'],
|
||||
check_str=base.RULE_SYSTEM_ADMIN_OR_OWNER,
|
||||
scope_types=['system', 'project'],
|
||||
description='Delete an application credential.',
|
||||
operations=[{'path': resource_path,
|
||||
'method': 'DELETE'}])
|
||||
'method': 'DELETE'}],
|
||||
deprecated_rule=deprecated_delete_application_credentials_for_user,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since=versionutils.deprecated.TRAIN)
|
||||
]
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@ from oslo_policy import policy
|
||||
|
||||
IDENTITY = 'identity:%s'
|
||||
RULE_ADMIN_REQUIRED = 'rule:admin_required'
|
||||
RULE_OWNER = 'user_id:%(user_id)s'
|
||||
RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner'
|
||||
RULE_ADMIN_OR_CREDENTIAL_OWNER = (
|
||||
'rule:admin_required or '
|
||||
@ -46,7 +47,8 @@ RULE_TRUST_OWNER = 'user_id:%(trust.trustor_user_id)s'
|
||||
SYSTEM_READER = 'role:reader and system_scope:all'
|
||||
SYSTEM_ADMIN = 'role:admin and system_scope:all'
|
||||
DOMAIN_READER = 'role:reader and domain_id:%(target.domain_id)s'
|
||||
|
||||
RULE_SYSTEM_ADMIN_OR_OWNER = '(' + SYSTEM_ADMIN + ') or rule:owner'
|
||||
RULE_SYSTEM_READER_OR_OWNER = '(' + SYSTEM_READER + ') or rule:owner'
|
||||
|
||||
rules = [
|
||||
policy.RuleDefault(
|
||||
@ -60,7 +62,7 @@ rules = [
|
||||
check_str='rule:admin_required or rule:service_role'),
|
||||
policy.RuleDefault(
|
||||
name='owner',
|
||||
check_str='user_id:%(user_id)s'),
|
||||
check_str=RULE_OWNER),
|
||||
policy.RuleDefault(
|
||||
name='admin_or_owner',
|
||||
check_str='rule:admin_required or rule:owner'),
|
||||
|
@ -422,6 +422,34 @@ def new_totp_credential(user_id, project_id=None, blob=None):
|
||||
return credential
|
||||
|
||||
|
||||
def new_application_credential_ref(roles=None,
|
||||
name=None,
|
||||
expires=None,
|
||||
secret=None):
|
||||
ref = {
|
||||
'id': uuid.uuid4().hex,
|
||||
'name': uuid.uuid4().hex,
|
||||
'description': uuid.uuid4().hex,
|
||||
}
|
||||
if roles:
|
||||
ref['roles'] = roles
|
||||
if secret:
|
||||
ref['secret'] = secret
|
||||
|
||||
if isinstance(expires, six.string_types):
|
||||
ref['expires_at'] = expires
|
||||
elif isinstance(expires, dict):
|
||||
ref['expires_at'] = (
|
||||
timeutils.utcnow() + datetime.timedelta(**expires)
|
||||
).strftime(TIME_FORMAT)
|
||||
elif expires is None:
|
||||
pass
|
||||
else:
|
||||
raise NotImplementedError('Unexpected value for "expires"')
|
||||
|
||||
return ref
|
||||
|
||||
|
||||
def new_role_ref(**kwargs):
|
||||
ref = {
|
||||
'id': uuid.uuid4().hex,
|
||||
|
665
keystone/tests/unit/protection/v3/test_application_credential.py
Normal file
665
keystone/tests/unit/protection/v3/test_application_credential.py
Normal file
@ -0,0 +1,665 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from six.moves import http_client
|
||||
|
||||
from keystone.common.policies import base as base_policy
|
||||
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
|
||||
|
||||
|
||||
class _TestAppCredBase(base_classes.TestCaseWithBootstrap):
|
||||
"""Base class for application credential tests."""
|
||||
|
||||
def _new_app_cred_data(self, user_id=None, project_id=None, name=None,
|
||||
expires=None, system=None):
|
||||
if not user_id:
|
||||
user_id = self.app_cred_user_id
|
||||
if not name:
|
||||
name = uuid.uuid4().hex
|
||||
if not expires:
|
||||
expires = datetime.datetime.utcnow() + datetime.timedelta(days=365)
|
||||
if not system:
|
||||
system = uuid.uuid4().hex
|
||||
if not project_id:
|
||||
project_id = self.app_cred_project_id
|
||||
app_cred_data = {
|
||||
'id': uuid.uuid4().hex,
|
||||
'name': name,
|
||||
'description': uuid.uuid4().hex,
|
||||
'user_id': user_id,
|
||||
'project_id': project_id,
|
||||
'system': system,
|
||||
'expires_at': expires,
|
||||
'roles': [
|
||||
{'id': self.bootstrapper.member_role_id},
|
||||
],
|
||||
'secret': uuid.uuid4().hex,
|
||||
'unrestricted': False
|
||||
}
|
||||
return app_cred_data
|
||||
|
||||
def setUp(self):
|
||||
super(_TestAppCredBase, self).setUp()
|
||||
|
||||
# create a user and project for app cred testing
|
||||
new_user_ref = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
app_cred_user_ref = PROVIDERS.identity_api.create_user(
|
||||
new_user_ref
|
||||
)
|
||||
self.app_cred_user_id = app_cred_user_ref['id']
|
||||
self.app_cred_user_password = new_user_ref['password']
|
||||
app_cred_project_ref = PROVIDERS.resource_api.create_project(
|
||||
uuid.uuid4().hex,
|
||||
unit.new_project_ref(domain_id=CONF.identity.default_domain_id)
|
||||
)
|
||||
self.app_cred_project_id = app_cred_project_ref['id']
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.member_role_id,
|
||||
user_id=self.app_cred_user_id,
|
||||
project_id=self.app_cred_project_id
|
||||
)
|
||||
|
||||
def _create_application_credential(self):
|
||||
app_cred = self._new_app_cred_data()
|
||||
return \
|
||||
PROVIDERS.application_credential_api.create_application_credential(
|
||||
app_cred)
|
||||
|
||||
def _override_policy(self):
|
||||
# TODO(gyee): Remove this once the deprecated policies in
|
||||
# keystone.common.policies.application_credential 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_application_credential': (
|
||||
base_policy.RULE_SYSTEM_READER_OR_OWNER),
|
||||
'identity:list_application_credentials': (
|
||||
base_policy.RULE_SYSTEM_READER_OR_OWNER),
|
||||
'identity:create_application_credential': (
|
||||
base_policy.RULE_OWNER),
|
||||
'identity:delete_application_credential': (
|
||||
base_policy.RULE_SYSTEM_ADMIN_OR_OWNER),
|
||||
}
|
||||
f.write(jsonutils.dumps(overridden_policies))
|
||||
|
||||
|
||||
class _DomainAndProjectUserTests(object):
|
||||
"""Domain and project user tests.
|
||||
|
||||
Domain and project users should not be able to manage application
|
||||
credentials other then their own.
|
||||
"""
|
||||
|
||||
def test_user_cannot_list_application_credentials(self):
|
||||
# create a couple of application credentials
|
||||
self._create_application_credential()
|
||||
self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
c.get('/v3/users/%s/application_credentials' % (
|
||||
self.app_cred_user_id),
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_get_application_credential(self):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
c.get('/v3/users/%s/application_credentials/%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['id']),
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_lookup_application_credential(self):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
c.get('/v3/users/%s/application_credentials?name=%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['name']),
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_delete_application_credential(self):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
c.delete(
|
||||
'/v3/users/%s/application_credentials/%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['id']),
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_lookup_non_existent_application_credential(self):
|
||||
with self.test_client() as c:
|
||||
c.get('/v3/users/%s/application_credentials?name=%s' % (
|
||||
self.app_cred_user_id,
|
||||
uuid.uuid4().hex),
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_create_app_credential_for_another_user(self):
|
||||
# create another user
|
||||
another_user = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
another_user_id = PROVIDERS.identity_api.create_user(
|
||||
another_user
|
||||
)['id']
|
||||
|
||||
app_cred_body = {
|
||||
'application_credential': unit.new_application_credential_ref(
|
||||
roles=[{'id': self.bootstrapper.member_role_id}])
|
||||
}
|
||||
|
||||
with self.test_client() as c:
|
||||
c.post(
|
||||
'/v3/users/%s/application_credentials' % another_user_id,
|
||||
json=app_cred_body,
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
|
||||
class _SystemUserAndOwnerTests(object):
|
||||
"""Common default functionality for all system users and owner."""
|
||||
|
||||
def test_user_can_list_application_credentials(self):
|
||||
# create a couple of application credentials
|
||||
self._create_application_credential()
|
||||
self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
r = c.get(
|
||||
'/v3/users/%s/application_credentials' % (
|
||||
self.app_cred_user_id),
|
||||
headers=self.headers)
|
||||
self.assertEqual(2, len(r.json['application_credentials']))
|
||||
|
||||
def test_user_can_get_application_credential(self):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
r = c.get(
|
||||
'/v3/users/%s/application_credentials/%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['id']),
|
||||
headers=self.headers)
|
||||
actual_app_cred = r.json['application_credential']
|
||||
self.assertEqual(app_cred['id'], actual_app_cred['id'])
|
||||
|
||||
def test_user_can_lookup_application_credential(self):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
r = c.get(
|
||||
'/v3/users/%s/application_credentials?name=%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['name']),
|
||||
headers=self.headers)
|
||||
self.assertEqual(1, len(r.json['application_credentials']))
|
||||
actual_app_cred = r.json['application_credentials'][0]
|
||||
self.assertEqual(app_cred['id'], actual_app_cred['id'])
|
||||
|
||||
def _test_delete_application_credential(
|
||||
self,
|
||||
expected_status_code=http_client.NO_CONTENT):
|
||||
app_cred = self._create_application_credential()
|
||||
|
||||
with self.test_client() as c:
|
||||
c.delete(
|
||||
'/v3/users/%s/application_credentials/%s' % (
|
||||
self.app_cred_user_id,
|
||||
app_cred['id']),
|
||||
expected_status_code=expected_status_code,
|
||||
headers=self.headers)
|
||||
|
||||
def test_user_cannot_create_app_credential_for_another_user(self):
|
||||
# create another user
|
||||
another_user = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
another_user_id = PROVIDERS.identity_api.create_user(
|
||||
another_user
|
||||
)['id']
|
||||
|
||||
app_cred_body = {
|
||||
'application_credential': unit.new_application_credential_ref(
|
||||
roles=[{'id': self.bootstrapper.member_role_id}])
|
||||
}
|
||||
|
||||
with self.test_client() as c:
|
||||
c.post(
|
||||
'/v3/users/%s/application_credentials' % another_user_id,
|
||||
json=app_cred_body,
|
||||
expected_status_code=http_client.FORBIDDEN,
|
||||
headers=self.headers)
|
||||
|
||||
|
||||
class SystemReaderTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_SystemUserAndOwnerTests):
|
||||
|
||||
def setUp(self):
|
||||
super(SystemReaderTests, self).setUp()
|
||||
self.loadapp()
|
||||
self.useFixture(ksfixtures.Policy(self.config_fixture))
|
||||
self.config_fixture.config(group='oslo_policy', enforce_scope=True)
|
||||
|
||||
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_system_reader_cannot_delete_application_credential_for_user(self):
|
||||
self._test_delete_application_credential(
|
||||
expected_status_code=http_client.FORBIDDEN)
|
||||
|
||||
|
||||
class SystemMemberTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_SystemUserAndOwnerTests):
|
||||
|
||||
def setUp(self):
|
||||
super(SystemMemberTests, self).setUp()
|
||||
self.loadapp()
|
||||
self.useFixture(ksfixtures.Policy(self.config_fixture))
|
||||
self.config_fixture.config(group='oslo_policy', enforce_scope=True)
|
||||
|
||||
system_member = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(
|
||||
system_member
|
||||
)['id']
|
||||
PROVIDERS.assignment_api.create_system_grant_for_user(
|
||||
self.user_id, self.bootstrapper.member_role_id
|
||||
)
|
||||
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=system_member['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_system_reader_cannot_delete_application_credential_for_user(self):
|
||||
self._test_delete_application_credential(
|
||||
expected_status_code=http_client.FORBIDDEN)
|
||||
|
||||
|
||||
class SystemAdminTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_SystemUserAndOwnerTests):
|
||||
|
||||
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_system_admin_can_delete_application_credential_for_user(self):
|
||||
self._test_delete_application_credential()
|
||||
|
||||
|
||||
class OwnerTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_SystemUserAndOwnerTests):
|
||||
|
||||
def setUp(self):
|
||||
super(OwnerTests, 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)
|
||||
|
||||
# in this case app_cred_user_id and user_id are the same since we
|
||||
# are testing the owner
|
||||
self.user_id = self.app_cred_user_id
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id,
|
||||
password=self.app_cred_user_password,
|
||||
project_id=self.app_cred_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 test_create_application_credential_by_owner(self):
|
||||
app_cred_body = {
|
||||
'application_credential': unit.new_application_credential_ref()
|
||||
}
|
||||
|
||||
with self.test_client() as c:
|
||||
c.post(
|
||||
'/v3/users/%s/application_credentials' % self.user_id,
|
||||
json=app_cred_body,
|
||||
expected_status_code=http_client.CREATED,
|
||||
headers=self.headers)
|
||||
|
||||
def test_owner_can_delete_application_credential(self):
|
||||
self._test_delete_application_credential()
|
||||
|
||||
|
||||
class DomainAdminTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
def setUp(self):
|
||||
super(DomainAdminTests, 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_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(domain_admin)['id']
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.admin_role_id, user_id=self.user_id,
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=domain_admin['password'],
|
||||
domain_id=CONF.identity.default_domain_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 DomainReaderTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
def setUp(self):
|
||||
super(DomainReaderTests, 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_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(domain_admin)['id']
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.reader_role_id, user_id=self.user_id,
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=domain_admin['password'],
|
||||
domain_id=CONF.identity.default_domain_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 DomainMemberTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
def setUp(self):
|
||||
super(DomainMemberTests, 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_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(domain_admin)['id']
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.member_role_id, user_id=self.user_id,
|
||||
domain_id=CONF.identity.default_domain_id
|
||||
)
|
||||
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=domain_admin['password'],
|
||||
domain_id=CONF.identity.default_domain_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(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
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)
|
||||
|
||||
project_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(project_admin)['id']
|
||||
# even project admin of project where the app credential
|
||||
# is intended for cannot perform app credential operations
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.admin_role_id,
|
||||
user_id=self.user_id,
|
||||
project_id=self.app_cred_project_id
|
||||
)
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=project_admin['password'],
|
||||
project_id=self.app_cred_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 ProjectReaderTests(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
def setUp(self):
|
||||
super(ProjectReaderTests, 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)
|
||||
|
||||
project_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(project_admin)['id']
|
||||
# even project admin of project where the app credential
|
||||
# is intended for cannot perform app credential operations
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.reader_role_id,
|
||||
user_id=self.user_id,
|
||||
project_id=self.app_cred_project_id
|
||||
)
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=project_admin['password'],
|
||||
project_id=self.app_cred_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(_TestAppCredBase,
|
||||
common_auth.AuthTestMixin,
|
||||
_DomainAndProjectUserTests):
|
||||
|
||||
def setUp(self):
|
||||
super(ProjectMemberTests, 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)
|
||||
|
||||
project_admin = unit.new_user_ref(
|
||||
domain_id=CONF.identity.default_domain_id)
|
||||
self.user_id = PROVIDERS.identity_api.create_user(project_admin)['id']
|
||||
# even project admin of project where the app credential
|
||||
# is intended for cannot perform app credential operations
|
||||
PROVIDERS.assignment_api.create_grant(
|
||||
self.bootstrapper.member_role_id,
|
||||
user_id=self.user_id,
|
||||
project_id=self.app_cred_project_id
|
||||
)
|
||||
auth = self.build_authentication_request(
|
||||
user_id=self.user_id, password=project_admin['password'],
|
||||
project_id=self.app_cred_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}
|
41
releasenotes/notes/bug-1818725-96d698e22e648764.yaml
Normal file
41
releasenotes/notes/bug-1818725-96d698e22e648764.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
[`bug 1818725 <https://bugs.launchpad.net/keystone/+bug/1818725>`_]
|
||||
[`bug 1750615 <https://bugs.launchpad.net/keystone/+bug/1750615>`_]
|
||||
The application credential API now supports the ``admin``, ``member``, and
|
||||
``reader`` default roles.
|
||||
|
||||
upgrade:
|
||||
- |
|
||||
[`bug 1818725 <https://bugs.launchpad.net/keystone/+bug/1818725>`_]
|
||||
[`bug 1750615 <https://bugs.launchpad.net/keystone/+bug/1750615>`_]
|
||||
The application credential API uses new default policies to make it more
|
||||
accessible to end users and administrators in a secure way. Please
|
||||
consider these new defaults if your deployment overrides application
|
||||
credential policies.
|
||||
deprecations:
|
||||
- |
|
||||
[`bug 1818725 <https://bugs.launchpad.net/keystone/+bug/1818725>`_]
|
||||
[`bug 1750615 <https://bugs.launchpad.net/keystone/+bug/1750615>`_]
|
||||
The application credential policies have been deprecated. The
|
||||
``identity:get_application_credential`` policy now uses
|
||||
``(role:reader and system_scope:all) or user_id:%(user_id)s`` instead of
|
||||
``rule:admin_required or user_id:%(user_id)s``. The
|
||||
``identity:list_application_credentials`` policy now uses
|
||||
``(role:reader and system_scope:all) or user_id:%(user_id)s`` instead of
|
||||
``rule:admin_required or user_id:%(user_id)s``. The
|
||||
``identity:delete_application_credential`` policy now use
|
||||
``(role:admin and system_scope:all) or user_id:%(user_id)s`` instead of
|
||||
``rule:admin_required or user_id:%(user_id)s``.
|
||||
These new defaults automatically account for system-scope and support
|
||||
a read-only role, making it easier for system administrators to delegate
|
||||
subsets of responsibility without compromising security. Please consider
|
||||
these new defaults if your deployment overrides the application
|
||||
credential policies.
|
||||
security:
|
||||
- |
|
||||
[`bug 1818725 <https://bugs.launchpad.net/keystone/+bug/1818725>`_]
|
||||
[`bug 1750615 <https://bugs.launchpad.net/keystone/+bug/1750615>`_]
|
||||
The application credential API now uses system-scope and default roles
|
||||
to provide better accessibility to users in a secure manner.
|
Loading…
Reference in New Issue
Block a user