diff --git a/keystone/api/trusts.py b/keystone/api/trusts.py index 1573e209d9..9ac1718004 100644 --- a/keystone/api/trusts.py +++ b/keystone/api/trusts.py @@ -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']: diff --git a/keystone/tests/unit/protection/v3/test_trusts.py b/keystone/tests/unit/protection/v3/test_trusts.py new file mode 100644 index 0000000000..15b0902734 --- /dev/null +++ b/keystone/tests/unit/protection/v3/test_trusts.py @@ -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 + )