Add protection tests for trusts API

Currently, the majority of access control enforcement for the trusts API
is not done in policy, but hardcoded in the controller logic. The
default policy check strings for these routes are empty. Before we can
enable system scope and default roles through the trusts policy, we need
to replicate the existing access control in policy. To do that, we
should test how it currently works. This patch adds those tests.

The trusts API is mostly only useable by the trustor or trustee. Mostly,
admins can't perform trust actions on behalf of the trustor or trustee.
The exception is for the delete action, but only when the is_admin
context is set. This change also fixes a minor regression where the
is_admin admin could not perform this action due to the auth_context not
being populated.

Change-Id: I6a5eca8240aa905e02fbf9bec335996c3a4f1c79
Partial-bug: #1818846
Partial-bug: #1818850
This commit is contained in:
Colleen Murphy 2019-08-09 21:37:23 -07:00
parent 6dc9b68595
commit 5086709ae2
2 changed files with 505 additions and 0 deletions

View File

@ -85,6 +85,8 @@ class TrustResource(ks_flask.ResourceBase):
json_home_parameter_rel_func = _build_parameter_relation
def _check_unrestricted(self):
if self.oslo_context.is_admin:
return
token = self.auth_context['token']
if 'application_credential' in token.methods:
if not token.application_credential['unrestricted']:

View File

@ -0,0 +1,503 @@
# 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 uuid
from six.moves import http_client
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
CONF = keystone.conf.CONF
PROVIDERS = provider_api.ProviderAPIs
class TrustTests(base_classes.TestCaseWithBootstrap,
common_auth.AuthTestMixin):
"""Common functionality for all trust tests.
Sets up trustor and trustee users and trust.
"""
def setUp(self):
super(TrustTests, self).setUp()
self.loadapp()
self.useFixture(ksfixtures.Policy(self.config_fixture))
domain = PROVIDERS.resource_api.create_domain(
uuid.uuid4().hex, unit.new_domain_ref()
)
self.domain_id = domain['id']
trustor_user = unit.new_user_ref(domain_id=self.domain_id)
self.trustor_user_id = PROVIDERS.identity_api.create_user(
trustor_user)['id']
trustee_user = unit.new_user_ref(domain_id=self.domain_id)
self.trustee_user_id = PROVIDERS.identity_api.create_user(
trustee_user)['id']
project = PROVIDERS.resource_api.create_project(
uuid.uuid4().hex, unit.new_project_ref(domain_id=self.domain_id)
)
self.project_id = project['id']
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.member_role_id, user_id=self.trustor_user_id,
project_id=self.project_id
)
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.member_role_id, user_id=self.trustee_user_id,
project_id=project['id']
)
self.trust_id = uuid.uuid4().hex
self.trust_data = {
'trust': {'trustor_user_id': self.trustor_user_id,
'trustee_user_id': self.trustee_user_id,
'project_id': self.project_id,
'impersonation': False},
'roles': [{"id": self.bootstrapper.member_role_id}]
}
auth = self.build_authentication_request(
user_id=self.trustor_user_id,
password=trustor_user['password'],
project_id=project['id']
)
# Grab a token using the trustor 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.trustor_headers = {'X-Auth-Token': self.token_id}
auth = self.build_authentication_request(
user_id=self.trustee_user_id,
password=trustee_user['password'],
project_id=project['id']
)
# Grab a token using the trustee 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.trustee_headers = {'X-Auth-Token': self.token_id}
class _AdminTestsMixin(object):
"""Tests for all admin users.
This exercises both the is_admin user and users granted the admin role on
the system scope.
"""
def test_admin_cannot_create_trust_for_other_user(self):
json = {'trust': self.trust_data['trust']}
json['trust']['roles'] = self.trust_data['roles']
with self.test_client() as c:
c.post(
'/v3/OS-TRUST/trusts',
json=json,
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
def test_admin_list_all_trusts(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts',
headers=self.headers
)
self.assertEqual(1, len(r.json['trusts']))
def test_admin_cannot_get_trust_for_other_user(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % self.trust_id,
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
def test_admin_can_get_non_existent_trust_not_found(self):
trust_id = uuid.uuid4().hex
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % trust_id,
headers=self.headers,
expected_status_code=http_client.NOT_FOUND
)
def test_admin_cannot_list_trust_roles_for_other_user(self):
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' % self.trust_id,
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
def test_admin_cannot_get_trust_role_for_other_user(self):
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 AdminTokenTests(TrustTests, _AdminTestsMixin):
"""Tests for the is_admin user.
The Trusts API has hardcoded is_admin checks that we need to ensure are
preserved through the system-scope transition.
"""
def setUp(self):
super(AdminTokenTests, self).setUp()
self.config_fixture.config(admin_token='ADMIN')
self.headers = {'X-Auth-Token': 'ADMIN'}
def test_admin_can_delete_trust_for_other_user(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.headers,
expected_status_code=http_client.NO_CONTENT
)
class SystemAdminTests(TrustTests, _AdminTestsMixin):
"""Tests for system admin users."""
def setUp(self):
super(SystemAdminTests, self).setUp()
# TODO(cmurphy) enable enforce_scope when trust policies become
# system-scope aware
# 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_admin_cannot_delete_trust_for_other_user(self):
# only the is_admin admin can do this
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.headers,
expected_status_code=http_client.FORBIDDEN
)
class ProjectUserTests(TrustTests):
"""Tests for all project users."""
def setUp(self):
super(ProjectUserTests, self).setUp()
other_user = unit.new_user_ref(domain_id=self.domain_id)
self.other_user_id = PROVIDERS.identity_api.create_user(
other_user)['id']
PROVIDERS.assignment_api.create_grant(
self.bootstrapper.member_role_id, user_id=self.other_user_id,
project_id=self.project_id
)
auth = self.build_authentication_request(
user_id=self.other_user_id,
password=other_user['password'],
project_id=self.project_id
)
# Grab a token using another persona who has no trusts associated with
# them
with self.test_client() as c:
r = c.post('/v3/auth/tokens', json=auth)
self.token_id = r.headers['X-Subject-Token']
self.other_headers = {'X-Auth-Token': self.token_id}
def test_user_can_list_trusts_of_whom_they_are_the_trustor(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
('/v3/OS-TRUST/trusts?trustor_user_id=%s' %
self.trustor_user_id),
headers=self.trustor_headers
)
self.assertEqual(1, len(r.json['trusts']))
self.assertEqual(self.trust_id, r.json['trusts'][0]['id'])
def test_user_can_list_trusts_delegated_to_them(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
('/v3/OS-TRUST/trusts?trustee_user_id=%s' %
self.trustee_user_id),
headers=self.trustee_headers
)
self.assertEqual(1, len(r.json['trusts']))
self.assertEqual(self.trust_id, r.json['trusts'][0]['id'])
def test_trustor_cannot_list_trusts_for_trustee(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustee_user_id=%s' %
self.trustee_user_id),
headers=self.trustor_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_trustee_cannot_list_trusts_for_trustor(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustor_user_id=%s' %
self.trustor_user_id),
headers=self.trustee_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_list_trusts_for_other_trustor(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustor_user_id=%s' %
self.trustor_user_id),
headers=self.other_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_list_trusts_for_other_trustee(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
('/v3/OS-TRUST/trusts?trustee_user_id=%s' %
self.trustee_user_id),
headers=self.other_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_list_all_trusts(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts',
headers=self.trustee_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_get_another_users_trust(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.other_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_can_get_non_existent_trust_not_found(self):
trust_id = uuid.uuid4().hex
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % trust_id,
headers=self.other_headers,
expected_status_code=http_client.NOT_FOUND
)
def test_user_can_get_trust_of_whom_they_are_the_trustor(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.get(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.trustor_headers
)
def test_user_can_get_trust_delegated_to_them(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.trustee_headers
)
self.assertEqual(r.json['trust']['id'], self.trust_id)
def test_trustor_can_create_trust(self):
json = {'trust': self.trust_data['trust']}
json['trust']['roles'] = self.trust_data['roles']
with self.test_client() as c:
c.post(
'/v3/OS-TRUST/trusts',
json=json,
headers=self.trustor_headers
)
def test_trustee_cannot_create_trust(self):
json = {'trust': self.trust_data['trust']}
json['trust']['roles'] = self.trust_data['roles']
with self.test_client() as c:
c.post(
'/v3/OS-TRUST/trusts',
json=json,
headers=self.trustee_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_trustor_can_delete_trust(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.trustor_headers
)
def test_trustee_cannot_delete_trust(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.trustee_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_user_cannot_delete_trust_for_other_user(self):
ref = PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
c.delete(
'/v3/OS-TRUST/trusts/%s' % ref['id'],
headers=self.other_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_trustor_can_list_trust_roles(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts/%s/roles' % self.trust_id,
headers=self.trustor_headers
)
self.assertEqual(r.json['roles'][0]['id'],
self.bootstrapper.member_role_id)
def test_trustee_can_list_trust_roles(self):
PROVIDERS.trust_api.create_trust(
self.trust_id, **self.trust_data)
with self.test_client() as c:
r = c.get(
'/v3/OS-TRUST/trusts/%s/roles' % self.trust_id,
headers=self.trustee_headers
)
self.assertEqual(r.json['roles'][0]['id'],
self.bootstrapper.member_role_id)
def test_user_cannot_list_trust_roles_for_other_user(self):
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' % self.trust_id,
headers=self.other_headers,
expected_status_code=http_client.FORBIDDEN
)
def test_trustor_can_get_trust_role(self):
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(self):
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_for_other_user(self):
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
)