diff options
Diffstat (limited to 'keystone-moon/keystone/tests')
80 files changed, 3558 insertions, 1449 deletions
diff --git a/keystone-moon/keystone/tests/unit/__init__.py b/keystone-moon/keystone/tests/unit/__init__.py index 837afe69..52af8dfc 100644 --- a/keystone-moon/keystone/tests/unit/__init__.py +++ b/keystone-moon/keystone/tests/unit/__init__.py @@ -13,22 +13,6 @@ # under the License. import oslo_i18n -import six - - -if six.PY3: - # NOTE(dstanek): This block will monkey patch libraries that are not - # yet supported in Python3. We do this that that it is possible to - # execute any tests at all. Without monkey patching modules the - # tests will fail with import errors. - - import sys - from unittest import mock # noqa: our import detection is naive? - - sys.modules['ldappool'] = mock.Mock() - sys.modules['memcache'] = mock.Mock() - sys.modules['oslo_messaging'] = mock.Mock() - sys.modules['paste'] = mock.Mock() # NOTE(dstanek): oslo_i18n.enable_lazy() must be called before # keystone.i18n._() is called to ensure it has the desired lazy lookup diff --git a/keystone-moon/keystone/tests/unit/backend/core_ldap.py b/keystone-moon/keystone/tests/unit/backend/core_ldap.py index a6cd0802..869bb620 100644 --- a/keystone-moon/keystone/tests/unit/backend/core_ldap.py +++ b/keystone-moon/keystone/tests/unit/backend/core_ldap.py @@ -17,7 +17,7 @@ from oslo_config import cfg from keystone.common import cache from keystone.common import ldap as common_ldap from keystone.common.ldap import core as common_ldap_core -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import fakeldap from keystone.tests.unit.ksfixtures import database @@ -66,7 +66,7 @@ class BaseBackendLdapCommon(object): def config_files(self): config_files = super(BaseBackendLdapCommon, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def get_user_enabled_vals(self, user): @@ -99,13 +99,13 @@ class BaseBackendLdap(object): super(BaseBackendLdap, self).load_fixtures(fixtures) -class BaseBackendLdapIdentitySqlEverythingElse(tests.SQLDriverOverrides): +class BaseBackendLdapIdentitySqlEverythingElse(unit.SQLDriverOverrides): """Mixin base for Identity LDAP, everything else SQL backend tests.""" def config_files(self): config_files = super(BaseBackendLdapIdentitySqlEverythingElse, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_sql.conf')) return config_files def setUp(self): diff --git a/keystone-moon/keystone/tests/unit/backend/core_sql.py b/keystone-moon/keystone/tests/unit/backend/core_sql.py index 9cbd858e..8c9f4957 100644 --- a/keystone-moon/keystone/tests/unit/backend/core_sql.py +++ b/keystone-moon/keystone/tests/unit/backend/core_sql.py @@ -13,12 +13,12 @@ import sqlalchemy from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class BaseBackendSqlTests(tests.SQLDriverOverrides, tests.TestCase): +class BaseBackendSqlTests(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(BaseBackendSqlTests, self).setUp() @@ -32,7 +32,7 @@ class BaseBackendSqlTests(tests.SQLDriverOverrides, tests.TestCase): def config_files(self): config_files = super(BaseBackendSqlTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files diff --git a/keystone-moon/keystone/tests/unit/backend/domain_config/core.py b/keystone-moon/keystone/tests/unit/backend/domain_config/core.py index c53d99b7..7bbbf313 100644 --- a/keystone-moon/keystone/tests/unit/backend/domain_config/core.py +++ b/keystone-moon/keystone/tests/unit/backend/domain_config/core.py @@ -17,7 +17,7 @@ import mock from testtools import matchers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit class DomainConfigTests(object): @@ -523,7 +523,7 @@ class DomainConfigTests(object): # The escaping '%' should have been removed self.assertEqual('my_url/%(password)s', res['ldap']['url']) - @tests.skip_if_cache_disabled('domain_config') + @unit.skip_if_cache_disabled('domain_config') def test_cache_layer_get_sensitive_config(self): config = {'ldap': {'url': uuid.uuid4().hex, 'user_tree_dn': uuid.uuid4().hex, @@ -549,3 +549,53 @@ class DomainConfigTests(object): {}, self.domain_config_api.get_config_with_sensitive_info( self.domain['id'])) + + def test_config_registration(self): + type = uuid.uuid4().hex + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.domain_config_api.release_registration( + self.domain['id'], type=type) + + # Make sure that once someone has it, nobody else can get it. + # This includes the domain who already has it. + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.assertFalse( + self.domain_config_api.obtain_registration( + self.domain['id'], type)) + + # Make sure we can read who does have it + self.assertEqual( + self.domain['id'], + self.domain_config_api.read_registration(type)) + + # Make sure releasing it is silent if the domain specified doesn't + # have the registration + domain2 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + self.resource_api.create_domain(domain2['id'], domain2) + self.domain_config_api.release_registration( + domain2['id'], type=type) + + # If nobody has the type registered, then trying to read it should + # raise ConfigRegistrationNotFound + self.domain_config_api.release_registration( + self.domain['id'], type=type) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type) + + # Finally check multiple registrations are cleared if you free the + # registration without specifying the type + type2 = uuid.uuid4().hex + self.domain_config_api.obtain_registration( + self.domain['id'], type) + self.domain_config_api.obtain_registration( + self.domain['id'], type2) + self.domain_config_api.release_registration(self.domain['id']) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type) + self.assertRaises(exception.ConfigRegistrationNotFound, + self.domain_config_api.read_registration, + type2) diff --git a/keystone-moon/keystone/tests/unit/backend/role/core.py b/keystone-moon/keystone/tests/unit/backend/role/core.py index f6e47fe9..d6e0d65c 100644 --- a/keystone-moon/keystone/tests/unit/backend/role/core.py +++ b/keystone-moon/keystone/tests/unit/backend/role/core.py @@ -16,7 +16,7 @@ import copy import uuid from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures @@ -87,7 +87,7 @@ class RoleTests(object): expected_role_ids = set(role['id'] for role in default_fixtures.ROLES) self.assertEqual(expected_role_ids, role_ids) - @tests.skip_if_cache_disabled('role') + @unit.skip_if_cache_disabled('role') def test_cache_layer_role_crud(self): role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} role_id = role['id'] diff --git a/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py b/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py index ba4b7c6e..44f2b612 100644 --- a/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py +++ b/keystone-moon/keystone/tests/unit/backend/role/test_ldap.py @@ -16,7 +16,7 @@ import uuid from oslo_config import cfg from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.backend import core_ldap from keystone.tests.unit.backend.role import core as core_role from keystone.tests.unit import default_fixtures @@ -35,7 +35,7 @@ class LdapRoleCommon(core_ldap.BaseBackendLdapCommon, core_role.RoleTests): pass -class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, tests.TestCase): +class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, unit.TestCase): """Test in an all-LDAP configuration. Include additional tests that are unique to LDAP (or need to be overridden) @@ -149,7 +149,7 @@ class LdapRole(LdapRoleCommon, core_ldap.BaseBackendLdap, tests.TestCase): class LdapIdentitySqlEverythingElseRole( core_ldap.BaseBackendLdapIdentitySqlEverythingElse, LdapRoleCommon, - tests.TestCase): + unit.TestCase): """Test Identity in LDAP, Everything else in SQL.""" pass diff --git a/keystone-moon/keystone/tests/unit/common/test_base64utils.py b/keystone-moon/keystone/tests/unit/common/test_base64utils.py index b0b75578..355a2e03 100644 --- a/keystone-moon/keystone/tests/unit/common/test_base64utils.py +++ b/keystone-moon/keystone/tests/unit/common/test_base64utils.py @@ -13,7 +13,7 @@ # under the License. from keystone.common import base64utils -from keystone.tests import unit as tests +from keystone.tests import unit base64_alphabet = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' @@ -26,7 +26,7 @@ base64url_alphabet = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' '-_=') # includes pad char -class TestValid(tests.BaseTestCase): +class TestValid(unit.BaseTestCase): def test_valid_base64(self): self.assertTrue(base64utils.is_valid_base64('+/==')) self.assertTrue(base64utils.is_valid_base64('+/+=')) @@ -68,7 +68,7 @@ class TestValid(tests.BaseTestCase): self.assertTrue(base64utils.is_valid_base64url('-_==')) -class TestBase64Padding(tests.BaseTestCase): +class TestBase64Padding(unit.BaseTestCase): def test_filter(self): self.assertEqual('', base64utils.filter_formatting('')) @@ -189,7 +189,7 @@ class TestBase64Padding(tests.BaseTestCase): base64utils.base64url_percent_decode, 'AB%3D%3') -class TestTextWrap(tests.BaseTestCase): +class TestTextWrap(unit.BaseTestCase): def test_wrapping(self): raw_text = 'abcdefgh' diff --git a/keystone-moon/keystone/tests/unit/common/test_injection.py b/keystone-moon/keystone/tests/unit/common/test_injection.py index b4c23a84..9a5d1e7d 100644 --- a/keystone-moon/keystone/tests/unit/common/test_injection.py +++ b/keystone-moon/keystone/tests/unit/common/test_injection.py @@ -15,10 +15,10 @@ import uuid from keystone.common import dependency -from keystone.tests import unit as tests +from keystone.tests import unit -class TestDependencyInjection(tests.BaseTestCase): +class TestDependencyInjection(unit.BaseTestCase): def setUp(self): super(TestDependencyInjection, self).setUp() dependency.reset() diff --git a/keystone-moon/keystone/tests/unit/common/test_json_home.py b/keystone-moon/keystone/tests/unit/common/test_json_home.py index fb7f8448..94e2d138 100644 --- a/keystone-moon/keystone/tests/unit/common/test_json_home.py +++ b/keystone-moon/keystone/tests/unit/common/test_json_home.py @@ -18,10 +18,10 @@ import copy from testtools import matchers from keystone.common import json_home -from keystone.tests import unit as tests +from keystone.tests import unit -class JsonHomeTest(tests.BaseTestCase): +class JsonHomeTest(unit.BaseTestCase): def test_build_v3_resource_relation(self): resource_name = self.getUniqueString() relation = json_home.build_v3_resource_relation(resource_name) diff --git a/keystone-moon/keystone/tests/unit/common/test_ldap.py b/keystone-moon/keystone/tests/unit/common/test_ldap.py index d3ce8cd2..e6e2c732 100644 --- a/keystone-moon/keystone/tests/unit/common/test_ldap.py +++ b/keystone-moon/keystone/tests/unit/common/test_ldap.py @@ -24,7 +24,7 @@ from testtools import matchers from keystone.common import driver_hints from keystone.common import ldap as ks_ldap from keystone.common.ldap import core as common_ldap_core -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import fakeldap @@ -32,7 +32,7 @@ from keystone.tests.unit import fakeldap CONF = cfg.CONF -class DnCompareTest(tests.BaseTestCase): +class DnCompareTest(unit.BaseTestCase): """Tests for the DN comparison functions in keystone.common.ldap.core.""" def test_prep(self): @@ -200,7 +200,7 @@ class DnCompareTest(tests.BaseTestCase): self.assertTrue(ks_ldap.dn_startswith(child, parent)) -class LDAPDeleteTreeTest(tests.TestCase): +class LDAPDeleteTreeTest(unit.TestCase): def setUp(self): super(LDAPDeleteTreeTest, self).setUp() @@ -223,7 +223,7 @@ class LDAPDeleteTreeTest(tests.TestCase): def config_files(self): config_files = super(LDAPDeleteTreeTest, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def test_deleteTree(self): @@ -283,7 +283,7 @@ class LDAPDeleteTreeTest(tests.TestCase): conn.search_s, grandchild_dn, ldap.SCOPE_BASE) -class SslTlsTest(tests.TestCase): +class SslTlsTest(unit.TestCase): """Tests for the SSL/TLS functionality in keystone.common.ldap.core.""" @mock.patch.object(ks_ldap.core.KeystoneLDAPHandler, 'simple_bind_s') @@ -350,7 +350,7 @@ class SslTlsTest(tests.TestCase): self.assertEqual(certdir, ldap.get_option(ldap.OPT_X_TLS_CACERTDIR)) -class LDAPPagedResultsTest(tests.TestCase): +class LDAPPagedResultsTest(unit.TestCase): """Tests the paged results functionality in keystone.common.ldap.core.""" def setUp(self): @@ -373,7 +373,7 @@ class LDAPPagedResultsTest(tests.TestCase): def config_files(self): config_files = super(LDAPPagedResultsTest, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files @mock.patch.object(fakeldap.FakeLdap, 'search_ext') @@ -390,7 +390,7 @@ class LDAPPagedResultsTest(tests.TestCase): 'objectclass=*') -class CommonLdapTestCase(tests.BaseTestCase): +class CommonLdapTestCase(unit.BaseTestCase): """These test cases call functions in keystone.common.ldap.""" def test_binary_attribute_values(self): @@ -497,7 +497,7 @@ class CommonLdapTestCase(tests.BaseTestCase): self.assertEqual(user_name, py_result[0][1]['user_name'][0]) -class LDAPFilterQueryCompositionTest(tests.TestCase): +class LDAPFilterQueryCompositionTest(unit.TestCase): """These test cases test LDAP filter generation.""" def setUp(self): diff --git a/keystone-moon/keystone/tests/unit/common/test_manager.py b/keystone-moon/keystone/tests/unit/common/test_manager.py new file mode 100644 index 00000000..1bc19763 --- /dev/null +++ b/keystone-moon/keystone/tests/unit/common/test_manager.py @@ -0,0 +1,39 @@ +# 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 mock + +from keystone import catalog +from keystone.common import manager +from keystone.tests import unit + + +class TestCreateLegacyDriver(unit.BaseTestCase): + + @mock.patch('oslo_log.versionutils.report_deprecated_feature') + def test_class_is_properly_deprecated(self, mock_reporter): + Driver = manager.create_legacy_driver(catalog.CatalogDriverV8) + + # NOTE(dstanek): I want to subvert the requirement for this + # class to implement all of the abstractmethods. + Driver.__abstractmethods__ = set() + impl = Driver() + + details = { + 'as_of': 'Liberty', + 'what': 'keystone.catalog.core.Driver', + 'in_favor_of': 'keystone.catalog.core.CatalogDriverV8', + 'remove_in': 'N', + } + mock_reporter.assert_called_with(mock.ANY, mock.ANY, details) + + self.assertIsInstance(impl, catalog.CatalogDriverV8) diff --git a/keystone-moon/keystone/tests/unit/common/test_notifications.py b/keystone-moon/keystone/tests/unit/common/test_notifications.py index 2d872733..ec087c41 100644 --- a/keystone-moon/keystone/tests/unit/common/test_notifications.py +++ b/keystone-moon/keystone/tests/unit/common/test_notifications.py @@ -1007,7 +1007,7 @@ class TestCallbackRegistration(unit.BaseTestCase): with mock.patch('keystone.notifications.LOG', self.mock_log): notifications.register_event_callback( - CREATED_OPERATION, 'thing', C.callback) + CREATED_OPERATION, 'thing', C().callback) callback = 'keystone.tests.unit.common.test_notifications.C.callback' expected_log_data = { @@ -1026,7 +1026,7 @@ class TestCallbackRegistration(unit.BaseTestCase): with mock.patch('keystone.notifications.LOG', self.mock_log): notifications.register_event_callback( - CREATED_OPERATION, 'thing', [callback, C.callback]) + CREATED_OPERATION, 'thing', [callback, C().callback]) callback_1 = 'keystone.tests.unit.common.test_notifications.callback' callback_2 = 'keystone.tests.unit.common.test_notifications.C.callback' diff --git a/keystone-moon/keystone/tests/unit/common/test_sql_core.py b/keystone-moon/keystone/tests/unit/common/test_sql_core.py index 1f33cfc3..b110ed08 100644 --- a/keystone-moon/keystone/tests/unit/common/test_sql_core.py +++ b/keystone-moon/keystone/tests/unit/common/test_sql_core.py @@ -14,7 +14,7 @@ from sqlalchemy.ext import declarative from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -27,7 +27,7 @@ class TestModel(ModelBase, sql.ModelDictMixin): text = sql.Column(sql.String(64), nullable=False) -class TestModelDictMixin(tests.BaseTestCase): +class TestModelDictMixin(unit.BaseTestCase): def test_creating_a_model_instance_from_a_dict(self): d = {'id': utils.new_uuid(), 'text': utils.new_uuid()} diff --git a/keystone-moon/keystone/tests/unit/common/test_utils.py b/keystone-moon/keystone/tests/unit/common/test_utils.py index e8bac3c0..d52eb729 100644 --- a/keystone-moon/keystone/tests/unit/common/test_utils.py +++ b/keystone-moon/keystone/tests/unit/common/test_utils.py @@ -20,7 +20,7 @@ from oslo_serialization import jsonutils from keystone.common import utils as common_utils from keystone import exception from keystone import service -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -29,7 +29,7 @@ CONF = cfg.CONF TZ = utils.TZ -class UtilsTestCase(tests.BaseTestCase): +class UtilsTestCase(unit.BaseTestCase): OPTIONAL = object() def setUp(self): @@ -154,11 +154,11 @@ class UtilsTestCase(tests.BaseTestCase): self.assertEqual(expected_json, json) -class ServiceHelperTests(tests.BaseTestCase): +class ServiceHelperTests(unit.BaseTestCase): @service.fail_gracefully def _do_test(self): raise Exception("Test Exc") def test_fail_gracefully(self): - self.assertRaises(tests.UnexpectedExit, self._do_test) + self.assertRaises(unit.UnexpectedExit, self._do_test) diff --git a/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py b/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py index a8b4ae76..5804f1c0 100644 --- a/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py +++ b/keystone-moon/keystone/tests/unit/contrib/federation/test_utils.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import uuid from keystone.auth.plugins import mapped from keystone.contrib.federation import utils as mapping_utils @@ -609,3 +610,25 @@ class MappingRuleEngineTests(unit.BaseTestCase): self.assertEqual(exp_user_name, mapped_properties['user']['name']) self.assertEqual('abc123%40example.com', mapped_properties['user']['id']) + + def test_whitelist_pass_through(self): + mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH + rp = mapping_utils.RuleProcessor(mapping['rules']) + assertion = mapping_fixtures.DEVELOPER_ASSERTION + mapped_properties = rp.process(assertion) + self.assertValidMappedUserObject(mapped_properties) + + self.assertEqual('developacct', mapped_properties['user']['name']) + self.assertEqual('Developer', + mapped_properties['group_names'][0]['name']) + + def test_type_not_in_assertion(self): + """Test that if the remote "type" is not in the assertion it fails.""" + mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_PASS_THROUGH + rp = mapping_utils.RuleProcessor(mapping['rules']) + assertion = {uuid.uuid4().hex: uuid.uuid4().hex} + mapped_properties = rp.process(assertion) + self.assertValidMappedUserObject(mapped_properties) + + self.assertNotIn('id', mapped_properties['user']) + self.assertNotIn('name', mapped_properties['user']) diff --git a/keystone-moon/keystone/tests/unit/core.py b/keystone-moon/keystone/tests/unit/core.py index e999b641..eb8b9f65 100644 --- a/keystone-moon/keystone/tests/unit/core.py +++ b/keystone-moon/keystone/tests/unit/core.py @@ -14,6 +14,7 @@ from __future__ import absolute_import import atexit +import datetime import functools import logging import os @@ -21,18 +22,21 @@ import re import shutil import socket import sys +import uuid import warnings import fixtures from oslo_config import cfg from oslo_config import fixture as config_fixture +from oslo_log import fixture as log_fixture from oslo_log import log +from oslo_utils import timeutils import oslotest.base as oslotest from oslotest import mockpatch +from paste.deploy import loadwsgi import six from sqlalchemy import exc from testtools import testcase -import webob # NOTE(ayoung) # environment.use_eventlet must run before any of the code that will @@ -82,6 +86,8 @@ rules.init() IN_MEM_DB_CONN_STRING = 'sqlite://' +TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' + exception._FATAL_EXCEPTION_FORMAT_ERRORS = True os.makedirs(TMPDIR) atexit.register(shutil.rmtree, TMPDIR) @@ -113,6 +119,26 @@ class dirs(object): DEFAULT_TEST_DB_FILE = dirs.tmp('test.db') +class EggLoader(loadwsgi.EggLoader): + _basket = {} + + def find_egg_entry_point(self, object_type, name=None): + egg_key = '%s:%s' % (object_type, name) + egg_ep = self._basket.get(egg_key) + if not egg_ep: + egg_ep = super(EggLoader, self).find_egg_entry_point( + object_type, name=name) + self._basket[egg_key] = egg_ep + return egg_ep + + +# NOTE(dstanek): class paths were remove from the keystone-paste.ini in +# favor of using entry points. This caused tests to slow to a crawl +# since we reload the application object for each RESTful test. This +# monkey-patching adds caching to paste deploy's egg lookup. +loadwsgi.EggLoader = EggLoader + + @atexit.register def remove_test_databases(): db = dirs.tmp('test.db') @@ -197,39 +223,135 @@ class UnexpectedExit(Exception): pass -class BadLog(Exception): - """Raised on invalid call to logging (parameter mismatch).""" - pass - - -class TestClient(object): - def __init__(self, app=None, token=None): - self.app = app - self.token = token - - def request(self, method, path, headers=None, body=None): - if headers is None: - headers = {} - - if self.token: - headers.setdefault('X-Auth-Token', self.token) - - req = webob.Request.blank(path) - req.method = method - for k, v in headers.items(): - req.headers[k] = v - if body: - req.body = body - return req.get_response(self.app) - - def get(self, path, headers=None): - return self.request('GET', path=path, headers=headers) - - def post(self, path, headers=None, body=None): - return self.request('POST', path=path, headers=headers, body=body) - - def put(self, path, headers=None, body=None): - return self.request('PUT', path=path, headers=headers, body=body) +def new_ref(): + """Populates a ref with attributes common to some API entities.""" + return { + 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, + 'description': uuid.uuid4().hex, + 'enabled': True} + + +def new_region_ref(): + ref = new_ref() + # Region doesn't have name or enabled. + del ref['name'] + del ref['enabled'] + ref['parent_region_id'] = None + return ref + + +def new_service_ref(): + ref = new_ref() + ref['type'] = uuid.uuid4().hex + return ref + + +def new_endpoint_ref(service_id, interface='public', default_region_id=None, + **kwargs): + ref = new_ref() + del ref['enabled'] # enabled is optional + ref['interface'] = interface + ref['service_id'] = service_id + ref['url'] = 'https://' + uuid.uuid4().hex + '.com' + ref['region_id'] = default_region_id + ref.update(kwargs) + return ref + + +def new_domain_ref(): + ref = new_ref() + return ref + + +def new_project_ref(domain_id=None, parent_id=None, is_domain=False): + ref = new_ref() + ref['domain_id'] = domain_id + ref['parent_id'] = parent_id + ref['is_domain'] = is_domain + return ref + + +def new_user_ref(domain_id, project_id=None): + ref = new_ref() + ref['domain_id'] = domain_id + ref['email'] = uuid.uuid4().hex + ref['password'] = uuid.uuid4().hex + if project_id: + ref['default_project_id'] = project_id + return ref + + +def new_group_ref(domain_id): + ref = new_ref() + ref['domain_id'] = domain_id + return ref + + +def new_credential_ref(user_id, project_id=None, cred_type=None): + ref = dict() + ref['id'] = uuid.uuid4().hex + ref['user_id'] = user_id + if cred_type == 'ec2': + ref['type'] = 'ec2' + ref['blob'] = uuid.uuid4().hex + else: + ref['type'] = 'cert' + ref['blob'] = uuid.uuid4().hex + if project_id: + ref['project_id'] = project_id + return ref + + +def new_role_ref(): + ref = new_ref() + # Roles don't have a description or the enabled flag + del ref['description'] + del ref['enabled'] + return ref + + +def new_policy_ref(): + ref = new_ref() + ref['blob'] = uuid.uuid4().hex + ref['type'] = uuid.uuid4().hex + return ref + + +def new_trust_ref(trustor_user_id, trustee_user_id, project_id=None, + impersonation=None, expires=None, role_ids=None, + role_names=None, remaining_uses=None, + allow_redelegation=False): + ref = dict() + ref['id'] = uuid.uuid4().hex + ref['trustor_user_id'] = trustor_user_id + ref['trustee_user_id'] = trustee_user_id + ref['impersonation'] = impersonation or False + ref['project_id'] = project_id + ref['remaining_uses'] = remaining_uses + ref['allow_redelegation'] = allow_redelegation + + 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"') + + role_ids = role_ids or [] + role_names = role_names or [] + if role_ids or role_names: + ref['roles'] = [] + for role_id in role_ids: + ref['roles'].append({'id': role_id}) + for role_name in role_names: + ref['roles'].append({'name': role_name}) + + return ref class BaseTestCase(oslotest.BaseTestCase): @@ -245,8 +367,7 @@ class BaseTestCase(oslotest.BaseTestCase): super(BaseTestCase, self).setUp() self.useFixture(mockpatch.PatchObject(sys, 'exit', side_effect=UnexpectedExit)) - self.useFixture(mockpatch.PatchObject(logging.Handler, 'handleError', - side_effect=BadLog)) + self.useFixture(log_fixture.get_logging_handle_error_fixture()) warnings.filterwarnings('error', category=DeprecationWarning, module='^keystone\\.') diff --git a/keystone-moon/keystone/tests/unit/identity/test_core.py b/keystone-moon/keystone/tests/unit/identity/test_core.py index fa95ec50..e9845401 100644 --- a/keystone-moon/keystone/tests/unit/identity/test_core.py +++ b/keystone-moon/keystone/tests/unit/identity/test_core.py @@ -22,20 +22,20 @@ from oslo_config import fixture as config_fixture from keystone import exception from keystone import identity -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database CONF = cfg.CONF -class TestDomainConfigs(tests.BaseTestCase): +class TestDomainConfigs(unit.BaseTestCase): def setUp(self): super(TestDomainConfigs, self).setUp() self.addCleanup(CONF.reset) - self.tmp_dir = tests.dirs.tmp() + self.tmp_dir = unit.dirs.tmp() self.config_fixture = self.useFixture(config_fixture.Config(CONF)) self.config_fixture.config(domain_config_dir=self.tmp_dir, @@ -125,7 +125,7 @@ class TestDomainConfigs(tests.BaseTestCase): self.assertEqual(3, load_driver_mock.call_count) -class TestDatabaseDomainConfigs(tests.TestCase): +class TestDatabaseDomainConfigs(unit.TestCase): def setUp(self): super(TestDatabaseDomainConfigs, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/ksfixtures/database.py b/keystone-moon/keystone/tests/unit/ksfixtures/database.py index 0012df74..6f23a99d 100644 --- a/keystone-moon/keystone/tests/unit/ksfixtures/database.py +++ b/keystone-moon/keystone/tests/unit/ksfixtures/database.py @@ -19,7 +19,7 @@ from oslo_config import cfg from oslo_db import options as db_options from keystone.common import sql -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF @@ -47,7 +47,7 @@ def initialize_sql_session(): # test cases. db_options.set_defaults( CONF, - connection=tests.IN_MEM_DB_CONN_STRING) + connection=unit.IN_MEM_DB_CONN_STRING) @run_once diff --git a/keystone-moon/keystone/tests/unit/mapping_fixtures.py b/keystone-moon/keystone/tests/unit/mapping_fixtures.py index f86d9245..94b07133 100644 --- a/keystone-moon/keystone/tests/unit/mapping_fixtures.py +++ b/keystone-moon/keystone/tests/unit/mapping_fixtures.py @@ -901,6 +901,290 @@ MAPPING_GROUPS_WHITELIST_AND_BLACKLIST = { ] } +# Mapping used by tokenless test cases, it maps the user_name +# and domain_name. +MAPPING_WITH_USERNAME_AND_DOMAINNAME = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'domain': { + 'name': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id +# and domain_name. +MAPPING_WITH_USERID_AND_DOMAINNAME = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'domain': { + 'name': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_name +# and domain_id. +MAPPING_WITH_USERNAME_AND_DOMAINID = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'domain': { + 'id': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id +# and domain_id. +MAPPING_WITH_USERID_AND_DOMAINID = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'domain': { + 'id': '{1}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + }, + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the domain_id only. +MAPPING_WITH_DOMAINID_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'domain': { + 'id': '{0}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_DOMAIN_ID' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the domain_name only. +MAPPING_WITH_DOMAINNAME_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'domain': { + 'name': '{0}' + }, + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_DOMAIN_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_name only. +MAPPING_WITH_USERNAME_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +# Mapping used by tokenless test cases, it maps the user_id only. +MAPPING_WITH_USERID_ONLY = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'id': '{0}', + 'type': 'local' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_ID' + } + ] + } + ] +} + +MAPPING_FOR_EPHEMERAL_USER = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}', + 'type': 'ephemeral' + }, + 'group': { + 'id': 'dummy' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +MAPPING_FOR_DEFAULT_EPHEMERAL_USER = { + 'rules': [ + { + 'local': [ + { + 'user': { + 'name': '{0}' + }, + 'group': { + 'id': 'dummy' + } + } + ], + 'remote': [ + { + 'type': 'SSL_CLIENT_USER_NAME' + } + ] + } + ] +} + +MAPPING_GROUPS_WHITELIST_PASS_THROUGH = { + "rules": [ + { + "remote": [ + { + "type": "UserName" + } + ], + "local": [ + { + "user": { + "name": "{0}", + "domain": { + "id": DEVELOPER_GROUP_DOMAIN_ID + } + } + } + ] + }, + { + "remote": [ + { + "type": "orgPersonType", + "whitelist": ['Developer'] + } + ], + "local": [ + { + "groups": "{0}", + "domain": { + "id": DEVELOPER_GROUP_DOMAIN_ID + } + } + ] + } + ] +} + + EMPLOYEE_ASSERTION = { 'Email': 'tim@example.com', 'UserName': 'tbo', diff --git a/keystone-moon/keystone/tests/unit/rest.py b/keystone-moon/keystone/tests/unit/rest.py index bfa52354..da24019f 100644 --- a/keystone-moon/keystone/tests/unit/rest.py +++ b/keystone-moon/keystone/tests/unit/rest.py @@ -13,15 +13,16 @@ # under the License. from oslo_serialization import jsonutils +from six.moves import http_client import webtest from keystone.auth import controllers as auth_controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class RestfulTestCase(tests.TestCase): +class RestfulTestCase(unit.TestCase): """Performs restful tests against the WSGI app over HTTP. This class launches public & admin WSGI servers for every test, which can @@ -113,7 +114,7 @@ class RestfulTestCase(tests.TestCase): example:: - self.assertResponseStatus(response, 204) + self.assertResponseStatus(response, http_client.NO_CONTENT) """ self.assertEqual( response.status_code, @@ -125,7 +126,8 @@ class RestfulTestCase(tests.TestCase): """Ensures that response headers appear as expected.""" self.assertIn('X-Auth-Token', response.headers.get('Vary')) - def assertValidErrorResponse(self, response, expected_status=400): + def assertValidErrorResponse(self, response, + expected_status=http_client.BAD_REQUEST): """Verify that the error response is valid. Subclasses can override this function based on the expected response. @@ -184,7 +186,8 @@ class RestfulTestCase(tests.TestCase): self._from_content_type(response, content_type=response_content_type) # we can save some code & improve coverage by always doing this - if method != 'HEAD' and response.status_code >= 400: + if (method != 'HEAD' and + response.status_code >= http_client.BAD_REQUEST): self.assertValidErrorResponse(response) # Contains the decoded response.body diff --git a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py index 9cde704e..4c574549 100644 --- a/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py +++ b/keystone-moon/keystone/tests/unit/test_associate_project_endpoint_extension.py @@ -15,6 +15,7 @@ import copy import uuid +from six.moves import http_client from testtools import matchers from keystone.tests.unit import test_v3 @@ -47,8 +48,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid endpoint and project id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) def test_create_endpoint_project_association_with_invalid_project(self): """PUT OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -60,7 +60,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_endpoint_project_association_with_invalid_endpoint(self): """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -72,7 +72,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_endpoint_project_association_with_unexpected_body(self): """PUT /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -81,8 +81,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ self.put(self.default_request_url, - body={'project_id': self.default_domain_project_id}, - expected_status=204) + body={'project_id': self.default_domain_project_id}) def test_check_endpoint_project_association(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -90,13 +89,11 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): Valid project and endpoint id test case. """ - self.put(self.default_request_url, - expected_status=204) + self.put(self.default_request_url) self.head('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_check_endpoint_project_association_with_invalid_project(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -109,7 +106,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_check_endpoint_project_association_with_invalid_endpoint(self): """HEAD /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -122,7 +119,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_endpoints_associated_with_valid_project(self): """GET /OS-EP-FILTER/projects/{project_id}/endpoints @@ -146,7 +143,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.put(self.default_request_url) self.get('/OS-EP-FILTER/projects/%(project_id)s/endpoints' % { 'project_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_projects_associated_with_endpoint(self): """GET /OS-EP-FILTER/endpoints/{endpoint_id}/projects @@ -168,8 +165,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ r = self.get('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % - {'endpoint_id': self.endpoint_id}, - expected_status=200) + {'endpoint_id': self.endpoint_id}) self.assertValidProjectListResponse(r, expected_length=0) def test_list_projects_associated_with_invalid_endpoint(self): @@ -180,7 +176,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): """ self.get('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_remove_endpoint_project_association(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -192,8 +188,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): self.delete('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) def test_remove_endpoint_project_association_with_invalid_project(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -206,7 +201,7 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': uuid.uuid4().hex, 'endpoint_id': self.endpoint_id}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_remove_endpoint_project_association_with_invalid_endpoint(self): """DELETE /OS-EP-FILTER/projects/{project_id}/endpoints/{endpoint_id} @@ -219,32 +214,32 @@ class EndpointFilterCRUDTestCase(TestExtensionCase): '/endpoints/%(endpoint_id)s' % { 'project_id': self.default_domain_project_id, 'endpoint_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_endpoint_project_association_cleanup_when_project_deleted(self): self.put(self.default_request_url) association_url = ('/OS-EP-FILTER/endpoints/%(endpoint_id)s/projects' % {'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=1) self.delete('/projects/%(project_id)s' % { 'project_id': self.default_domain_project_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidProjectListResponse(r, expected_length=0) def test_endpoint_project_association_cleanup_when_endpoint_deleted(self): self.put(self.default_request_url) association_url = '/OS-EP-FILTER/projects/%(project_id)s/endpoints' % { 'project_id': self.default_domain_project_id} - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=1) self.delete('/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}) - r = self.get(association_url, expected_status=200) + r = self.get(association_url) self.assertValidEndpointListResponse(r, expected_length=0) @@ -275,8 +270,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # attempt to authenticate without requesting a project auth_data = self.build_authentication_request( @@ -296,8 +290,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -317,8 +310,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -337,8 +329,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # create a second temporary endpoint self.endpoint_id2 = uuid.uuid4().hex @@ -352,8 +343,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id2}, - expected_status=204) + 'endpoint_id': self.endpoint_id2}) # remove the temporary reference # this will create inconsistency in the endpoint filter table @@ -379,8 +369,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) # Add a disabled endpoint to the default project. @@ -398,8 +387,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': disabled_endpoint_id}, - expected_status=204) + 'endpoint_id': disabled_endpoint_id}) # Authenticate to get token with catalog auth_data = self.build_authentication_request( @@ -428,13 +416,11 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id1}, - expected_status=204) + 'endpoint_id': endpoint_id1}) self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': endpoint_id2}, - expected_status=204) + 'endpoint_id': endpoint_id2}) # there should be only two endpoints in token catalog auth_data = self.build_authentication_request( @@ -453,8 +439,7 @@ class EndpointFilterTokenRequestTestCase(TestExtensionCase): self.put('/OS-EP-FILTER/projects/%(project_id)s' '/endpoints/%(endpoint_id)s' % { 'project_id': self.project['id'], - 'endpoint_id': self.endpoint_id}, - expected_status=204) + 'endpoint_id': self.endpoint_id}) auth_data = self.build_authentication_request( user_id=self.user['id'], @@ -589,7 +574,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): invalid_body['endpoint_group']['filters'] = {'foobar': 'admin'} self.post(self.DEFAULT_ENDPOINT_GROUP_URL, body=invalid_body, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_endpoint_group(self): """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -624,7 +609,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_check_endpoint_group(self): """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id} @@ -637,7 +622,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_invalid_endpoint_group(self): """HEAD /OS-EP-FILTER/endpoint_groups/{endpoint_group_id} @@ -648,7 +633,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_patch_endpoint_group(self): """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -685,7 +670,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): } url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': 'ABC'} - self.patch(url, body=body, expected_status=404) + self.patch(url, body=body, expected_status=http_client.NOT_FOUND) def test_patch_invalid_endpoint_group(self): """PATCH /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -707,7 +692,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.DEFAULT_ENDPOINT_GROUP_URL, self.DEFAULT_ENDPOINT_GROUP_BODY) url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.patch(url, body=body, expected_status=400) + self.patch(url, body=body, expected_status=http_client.BAD_REQUEST) # Perform a GET call to ensure that the content remains # the same (as DEFAULT_ENDPOINT_GROUP_BODY) after attempting to update @@ -731,7 +716,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_invalid_endpoint_group(self): """GET /OS-EP-FILTER/endpoint_groups/{endpoint_group} @@ -742,7 +727,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): endpoint_group_id = 'foobar' url = '/OS-EP-FILTER/endpoint_groups/%(endpoint_group_id)s' % { 'endpoint_group_id': endpoint_group_id} - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_add_endpoint_group_to_project(self): """Create a valid endpoint group and project association.""" @@ -761,7 +746,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.put(url, expected_status=404) + self.put(url, expected_status=http_client.NOT_FOUND) def test_get_endpoint_group_in_project(self): """Test retrieving project endpoint group association.""" @@ -787,7 +772,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_list_endpoint_groups_in_project(self): """GET /OS-EP-FILTER/projects/{project_id}/endpoint_groups.""" @@ -813,7 +798,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = ('/OS-EP-FILTER/projects/%(project_id)s/endpoint_groups' % {'project_id': project_id}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_empty_endpoint_groups_in_project(self): """Test when no endpoint groups associated with the project.""" @@ -831,7 +816,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): self.project_id) url = self._get_project_endpoint_group_url( endpoint_group_id, self.project_id) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_check_endpoint_group_to_project_with_invalid_project_id(self): """Test HEAD with an invalid endpoint group and project association.""" @@ -848,7 +833,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): project_id = uuid.uuid4().hex url = self._get_project_endpoint_group_url( endpoint_group_id, project_id) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_list_endpoint_groups(self): """GET /OS-EP-FILTER/endpoint_groups.""" @@ -992,7 +977,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # endpoint group association again self.delete('/projects/%(project_id)s' % { 'project_id': project['id']}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_endpoint_group_project_cleanup_with_endpoint_group(self): # create endpoint group @@ -1012,7 +997,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # now remove the project endpoint group association self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_removing_an_endpoint_group_project(self): # create an endpoint group @@ -1026,7 +1011,7 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): # remove the endpoint group project self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_remove_endpoint_group_with_project_association(self): # create an endpoint group @@ -1044,8 +1029,9 @@ class EndpointGroupCRUDTestCase(TestExtensionCase): '%(endpoint_group_id)s' % {'endpoint_group_id': endpoint_group_id}) self.delete(endpoint_group_url) - self.get(endpoint_group_url, expected_status=404) - self.get(project_endpoint_group_url, expected_status=404) + self.get(endpoint_group_url, expected_status=http_client.NOT_FOUND) + self.get(project_endpoint_group_url, + expected_status=http_client.NOT_FOUND) def _create_valid_endpoint_group(self, url, body): r = self.post(url, body=body) diff --git a/keystone-moon/keystone/tests/unit/test_auth.py b/keystone-moon/keystone/tests/unit/test_auth.py index f253b02d..6dd52c8a 100644 --- a/keystone-moon/keystone/tests/unit/test_auth.py +++ b/keystone-moon/keystone/tests/unit/test_auth.py @@ -29,7 +29,7 @@ from keystone.common import authorization from keystone import config from keystone import exception from keystone.models import token_model -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone import token @@ -72,7 +72,7 @@ def _build_user_auth(token=None, user_id=None, username=None, return auth_json -class AuthTest(tests.TestCase): +class AuthTest(unit.TestCase): def setUp(self): self.useFixture(database.Database()) super(AuthTest, self).setUp() @@ -460,6 +460,37 @@ class AuthWithToken(AuthTest): dict(is_admin=True, query_string={}), token_id=token_id) + def test_deleting_role_assignment_does_not_revoke_unscoped_token(self): + no_context = {} + admin_context = dict(is_admin=True, query_string={}) + + project = { + 'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex, + 'domain_id': DEFAULT_DOMAIN_ID} + self.resource_api.create_project(project['id'], project) + role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + self.role_api.create_role(role['id'], role) + self.assignment_api.add_role_to_user_and_project( + self.user_foo['id'], project['id'], role['id']) + + # Get an unscoped token. + token = self.controller.authenticate(no_context, _build_user_auth( + username=self.user_foo['name'], + password=self.user_foo['password'])) + token_id = token['access']['token']['id'] + + # Ensure it is valid + self.controller.validate_token(admin_context, token_id=token_id) + + # Delete the role assignment, which should not invalidate the token, + # because we're not consuming it with just an unscoped token. + self.assignment_api.remove_role_from_user_and_project( + self.user_foo['id'], project['id'], role['id']) + + # Ensure it is still valid + self.controller.validate_token(admin_context, token_id=token_id) + def test_only_original_audit_id_is_kept(self): context = {} @@ -1285,14 +1316,14 @@ class TokenExpirationTest(AuthTest): self._maintain_token_expiration() -class AuthCatalog(tests.SQLDriverOverrides, AuthTest): +class AuthCatalog(unit.SQLDriverOverrides, AuthTest): """Tests for the catalog provided in the auth response.""" def config_files(self): config_files = super(AuthCatalog, self).config_files() # We need to use a backend that supports disabled endpoints, like the # SQL backend. - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def _create_endpoints(self): @@ -1402,7 +1433,7 @@ class AuthCatalog(tests.SQLDriverOverrides, AuthTest): self.assertEqual(exp_endpoint, endpoint) -class NonDefaultAuthTest(tests.TestCase): +class NonDefaultAuthTest(unit.TestCase): def test_add_non_default_auth_method(self): self.config_fixture.config(group='auth', diff --git a/keystone-moon/keystone/tests/unit/test_auth_plugin.py b/keystone-moon/keystone/tests/unit/test_auth_plugin.py index a259cc2a..8dd22aa8 100644 --- a/keystone-moon/keystone/tests/unit/test_auth_plugin.py +++ b/keystone-moon/keystone/tests/unit/test_auth_plugin.py @@ -18,7 +18,7 @@ import mock from keystone import auth from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit # for testing purposes only @@ -37,7 +37,7 @@ class SimpleChallengeResponse(auth.AuthMethodHandler): return {"challenge": "What's the name of your high school?"} -class TestAuthPlugin(tests.SQLDriverOverrides, tests.TestCase): +class TestAuthPlugin(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(TestAuthPlugin, self).setUp() self.load_backends() @@ -121,11 +121,11 @@ class TestAuthPluginDynamicOptions(TestAuthPlugin): def config_files(self): config_files = super(TestAuthPluginDynamicOptions, self).config_files() - config_files.append(tests.dirs.tests_conf('test_auth_plugin.conf')) + config_files.append(unit.dirs.tests_conf('test_auth_plugin.conf')) return config_files -class TestMapped(tests.TestCase): +class TestMapped(unit.TestCase): def setUp(self): super(TestMapped, self).setUp() self.load_backends() @@ -134,7 +134,7 @@ class TestMapped(tests.TestCase): def config_files(self): config_files = super(TestMapped, self).config_files() - config_files.append(tests.dirs.tests_conf('test_auth_plugin.conf')) + config_files.append(unit.dirs.tests_conf('test_auth_plugin.conf')) return config_files def auth_plugin_config_override(self, methods=None, **method_classes): diff --git a/keystone-moon/keystone/tests/unit/test_backend.py b/keystone-moon/keystone/tests/unit/test_backend.py index 45b8e0b0..d3b51edd 100644 --- a/keystone-moon/keystone/tests/unit/test_backend.py +++ b/keystone-moon/keystone/tests/unit/test_backend.py @@ -28,7 +28,7 @@ from testtools import matchers from keystone.catalog import core from keystone.common import driver_hints from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import filtering from keystone.tests.unit import utils as test_utils @@ -40,7 +40,379 @@ DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id NULL_OBJECT = object() -class IdentityTests(object): +class AssignmentTestHelperMixin(object): + """Mixin class to aid testing of assignments. + + This class supports data driven test plans that enable: + + - Creation of initial entities, such as domains, users, groups, projects + and roles + - Creation of assignments referencing the above entities + - A set of input parameters and expected outputs to list_role_assignments + based on the above test data + + A test plan is a dict of the form: + + test_plan = { + entities: details and number of entities, + group_memberships: group-user entity memberships, + assignments: list of assignments to create, + tests: list of pairs of input params and expected outputs} + + An example test plan: + + test_plan = { + # First, create the entities required. Entities are specified by + # a dict with the key being the entity type and the value an + # entity specification which can be one of: + # + # - a simple number, e.g. {'users': 3} creates 3 users + # - a dict where more information regarding the contents of the entity + # is required, e.g. {'domains' : {'users : 3}} creates a domain + # with three users + # - a list of entity specifications if multiple are required + # + # The following creates a domain that contains a single user, group and + # project, as well as creating three roles. + + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + + # If it is required that an existing domain be used for the new + # entities, then the id of that domain can be included in the + # domain dict. For example, if alternatively we wanted to add 3 users + # to the default domain, add a second domain containing 3 projects as + # well as 5 additional empty domains, the entities would be defined as: + # + # 'entities': {'domains': [{'id': DEFAULT_DOMAIN, 'users': 3}, + # {'projects': 3}, 5]}, + # + # A project hierarchy can be specified within the 'projects' section by + # nesting the 'project' key, for example to create a project with three + # sub-projects you would use: + + 'projects': {'project': 3} + + # A more complex hierarchy can also be defined, for example the + # following would define three projects each containing a + # sub-project, each of which contain a further three sub-projects. + + 'projects': [{'project': {'project': 3}}, + {'project': {'project': 3}}, + {'project': {'project': 3}}] + + # A list of groups and their members. In this case make users with + # index 0 and 1 members of group with index 0. Users and Groups are + # indexed in the order they appear in the 'entities' key above. + + 'group_memberships': [{'group': 0, 'users': [0, 1]}] + + # Next, create assignments between the entities, referencing the + # entities by index, i.e. 'user': 0 refers to user[0]. Entities are + # indexed in the order they appear in the 'entities' key above within + # their entity type. + + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}], + + # Finally, define an array of tests where list_role_assignment() is + # called with the given input parameters and the results are then + # confirmed to be as given in 'results'. Again, all entities are + # referenced by index. + + 'tests': [ + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}, + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'user': 0, 'role': 2, 'project': 0}]}] + + # The 'params' key also supports the 'effective' and + # 'inherited_to_projects' options to list_role_assignments.} + + """ + def _handle_project_spec(self, test_data, domain_id, project_spec, + parent_id=None): + """Handle the creation of a project or hierarchy of projects. + + project_spec may either be a count of the number of projects to + create, or it may be a list of the form: + + [{'project': project_spec}, {'project': project_spec}, ...] + + This method is called recursively to handle the creation of a + hierarchy of projects. + + """ + def _create_project(domain_id, parent_id): + new_project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, + 'domain_id': domain_id, 'parent_id': parent_id} + new_project = self.resource_api.create_project(new_project['id'], + new_project) + return new_project + + if isinstance(project_spec, list): + for this_spec in project_spec: + self._handle_project_spec( + test_data, domain_id, this_spec, parent_id=parent_id) + elif isinstance(project_spec, dict): + new_proj = _create_project(domain_id, parent_id) + test_data['projects'].append(new_proj) + self._handle_project_spec( + test_data, domain_id, project_spec['project'], + parent_id=new_proj['id']) + else: + for _ in range(project_spec): + test_data['projects'].append( + _create_project(domain_id, parent_id)) + + def _handle_domain_spec(self, test_data, domain_spec): + """Handle the creation of domains and their contents. + + domain_spec may either be a count of the number of empty domains to + create, a dict describing the domain contents, or a list of + domain_specs. + + In the case when a list is provided, this method calls itself + recursively to handle the list elements. + + This method will insert any entities created into test_data + + """ + def _create_domain(domain_id=None): + if domain_id is None: + new_domain = {'id': uuid.uuid4().hex, + 'name': uuid.uuid4().hex} + self.resource_api.create_domain(new_domain['id'], + new_domain) + return new_domain + else: + # The test plan specified an existing domain to use + return self.resource_api.get_domain(domain_id) + + def _create_entity_in_domain(entity_type, domain_id): + """Create a user or group entity in the domain.""" + + new_entity = {'name': uuid.uuid4().hex, 'domain_id': domain_id} + if entity_type == 'users': + new_entity = self.identity_api.create_user(new_entity) + elif entity_type == 'groups': + new_entity = self.identity_api.create_group(new_entity) + else: + # Must be a bad test plan + raise exception.NotImplemented() + return new_entity + + if isinstance(domain_spec, list): + for x in domain_spec: + self._handle_domain_spec(test_data, x) + elif isinstance(domain_spec, dict): + # If there is a domain ID specified, then use it + the_domain = _create_domain(domain_spec.get('id')) + test_data['domains'].append(the_domain) + for entity_type, value in domain_spec.items(): + if entity_type == 'id': + # We already used this above to determine whether to + # use and existing domain + continue + if entity_type == 'projects': + # If it's projects, we need to handle the potential + # specification of a project hierarchy + self._handle_project_spec( + test_data, the_domain['id'], value) + else: + # It's a count of number of entities + for _ in range(value): + test_data[entity_type].append( + _create_entity_in_domain( + entity_type, the_domain['id'])) + else: + for _ in range(domain_spec): + test_data['domains'].append(_create_domain()) + + def create_entities(self, entity_pattern): + """Create the entities specified in the test plan. + + Process the 'entities' key in the test plan, creating the requested + entities. Each created entity will be added to the array of entities + stored in the returned test_data object, e.g.: + + test_data['users'] = [user[0], user[1]....] + + """ + def _create_role(): + new_role = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + return self.role_api.create_role(new_role['id'], new_role) + + test_data = {} + for entity in ['users', 'groups', 'domains', 'projects', 'roles']: + test_data[entity] = [] + + # Create any domains requested and, if specified, any entities within + # those domains + if 'domains' in entity_pattern: + self._handle_domain_spec(test_data, entity_pattern['domains']) + + # Create any roles requested + if 'roles' in entity_pattern: + for _ in range(entity_pattern['roles']): + test_data['roles'].append(_create_role()) + + return test_data + + def _convert_entity_shorthand(self, key, shorthand_data, reference_data): + """Convert a shorthand entity description into a full ID reference. + + In test plan definitions, we allow a shorthand for referencing to an + entity of the form: + + 'user': 0 + + which is actually shorthand for: + + 'user_id': reference_data['users'][0]['id'] + + This method converts the shorthand version into the full reference. + + """ + expanded_key = '%s_id' % key + reference_index = '%ss' % key + index_value = ( + reference_data[reference_index][shorthand_data[key]]['id']) + return expanded_key, index_value + + def create_group_memberships(self, group_pattern, test_data): + """Create the group memberships specified in the test plan.""" + + for group_spec in group_pattern: + # Each membership specification is a dict of the form: + # + # {'group': 0, 'users': [list of user indexes]} + # + # Add all users in the list to the specified group, first + # converting from index to full entity ID. + group_value = test_data['groups'][group_spec['group']]['id'] + for user_index in group_spec['users']: + user_value = test_data['users'][user_index]['id'] + self.identity_api.add_user_to_group(user_value, group_value) + return test_data + + def create_assignments(self, assignment_pattern, test_data): + """Create the assignments specified in the test plan.""" + + # First store how many assignments are already in the system, + # so during the tests we can check the number of new assignments + # created. + test_data['initial_assignment_count'] = ( + len(self.assignment_api.list_role_assignments())) + + # Now create the new assignments in the test plan + for assignment in assignment_pattern: + # Each assignment is a dict of the form: + # + # { 'user': 0, 'project':1, 'role': 6} + # + # where the value of each item is the index into the array of + # entities created earlier. + # + # We process the assignment dict to create the args required to + # make the create_grant() call. + args = {} + for param in assignment: + if param == 'inherited_to_projects': + args[param] = assignment[param] + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, assignment, test_data) + args[key] = value + self.assignment_api.create_grant(**args) + return test_data + + def execute_assignment_tests(self, test_plan, test_data): + """Execute the test plan, based on the created test_data.""" + + def check_results(expected, actual, param_arg_count): + if param_arg_count == 0: + # It was an unfiltered call, so default fixture assignments + # might be polluting our answer - so we take into account + # how many assignments there were before the test. + self.assertEqual( + len(expected) + test_data['initial_assignment_count'], + len(actual)) + else: + self.assertThat(actual, matchers.HasLength(len(expected))) + + for each_expected in expected: + expected_assignment = {} + for param in each_expected: + if param == 'inherited_to_projects': + expected_assignment[param] = each_expected[param] + elif param == 'indirect': + # We're expecting the result to contain an indirect + # dict with the details how the role came to be placed + # on this entity - so convert the key/value pairs of + # that dict into real entity references. + indirect_term = {} + for indirect_param in each_expected[param]: + key, value = self._convert_entity_shorthand( + indirect_param, each_expected[param], + test_data) + indirect_term[key] = value + expected_assignment[param] = indirect_term + else: + # Convert a simple shorthand entry into a full + # entity reference + key, value = self._convert_entity_shorthand( + param, each_expected, test_data) + expected_assignment[key] = value + self.assertIn(expected_assignment, actual) + + # Go through each test in the array, processing the input params, which + # we build into an args dict, and then call list_role_assignments. Then + # check the results against those specified in the test plan. + for test in test_plan.get('tests', []): + args = {} + for param in test['params']: + if param in ['effective', 'inherited']: + # Just pass the value into the args + args[param] = test['params'][param] + else: + # Turn 'entity : 0' into 'entity_id = ac6736ba873d' + # where entity in user, group, project or domain + key, value = self._convert_entity_shorthand( + param, test['params'], test_data) + args[key] = value + results = self.assignment_api.list_role_assignments(**args) + check_results(test['results'], results, len(args)) + + def execute_assignment_test_plan(self, test_plan): + """Create entities, assignments and execute the test plan. + + The standard method to call to create entities and assignments and + execute the tests as specified in the test_plan. The test_data + dict is returned so that, if required, the caller can execute + additional manual tests with the entities and assignments created. + + """ + test_data = self.create_entities(test_plan['entities']) + if 'group_memberships' in test_plan: + self.create_group_memberships(test_plan['group_memberships'], + test_data) + if 'assignments' in test_plan: + test_data = self.create_assignments(test_plan['assignments'], + test_data) + self.execute_assignment_tests(test_plan, test_data) + return test_data + + +class IdentityTests(AssignmentTestHelperMixin): def _get_domain_fixture(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} self.resource_api.create_domain(domain['id'], domain) @@ -227,7 +599,7 @@ class IdentityTests(object): self.user_foo.pop('password') self.assertDictEqual(user_ref, self.user_foo) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_get_user(self): user = { 'name': uuid.uuid4().hex.lower(), @@ -279,7 +651,7 @@ class IdentityTests(object): self.user_foo.pop('password') self.assertDictEqual(user_ref, self.user_foo) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_get_user_by_name(self): user = { 'name': uuid.uuid4().hex.lower(), @@ -506,168 +878,83 @@ class IdentityTests(object): 'fake2') def test_list_role_assignments_unfiltered(self): - """Test unfiltered listing of role assignments. - - Test Plan: - - - Create a domain, with a user, group & project - - Find how many role assignments already exist (from default - fixtures) - - Create a grant of each type (user/group on project/domain) - - Check the number of assignments has gone up by 4 and that - the entries we added are in the list returned - - Check that if we list assignments by role_id, then we get back - assignments that only contain that role. - - """ - new_domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} - self.resource_api.create_domain(new_domain['id'], new_domain) - new_user = {'name': uuid.uuid4().hex, 'password': uuid.uuid4().hex, - 'enabled': True, 'domain_id': new_domain['id']} - new_user = self.identity_api.create_user(new_user) - new_group = {'domain_id': new_domain['id'], 'name': uuid.uuid4().hex} - new_group = self.identity_api.create_group(new_group) - new_project = {'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'domain_id': new_domain['id']} - self.resource_api.create_project(new_project['id'], new_project) - - # First check how many role grants already exist - existing_assignments = len(self.assignment_api.list_role_assignments()) - - # Now create the grants (roles are defined in default_fixtures) - self.assignment_api.create_grant(user_id=new_user['id'], - domain_id=new_domain['id'], - role_id='member') - self.assignment_api.create_grant(user_id=new_user['id'], - project_id=new_project['id'], - role_id='other') - self.assignment_api.create_grant(group_id=new_group['id'], - domain_id=new_domain['id'], - role_id='admin') - self.assignment_api.create_grant(group_id=new_group['id'], - project_id=new_project['id'], - role_id='admin') - - # Read back the full list of assignments - check it is gone up by 4 - assignment_list = self.assignment_api.list_role_assignments() - self.assertEqual(existing_assignments + 4, len(assignment_list)) - - # Now check that each of our four new entries are in the list - self.assertIn( - {'user_id': new_user['id'], 'domain_id': new_domain['id'], - 'role_id': 'member'}, - assignment_list) - self.assertIn( - {'user_id': new_user['id'], 'project_id': new_project['id'], - 'role_id': 'other'}, - assignment_list) - self.assertIn( - {'group_id': new_group['id'], 'domain_id': new_domain['id'], - 'role_id': 'admin'}, - assignment_list) - self.assertIn( - {'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': 'admin'}, - assignment_list) + """Test unfiltered listing of role assignments.""" + + test_plan = { + # Create a domain, with a user, group & project + 'entities': {'domains': {'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that we get back the 4 assignments + {'params': {}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + self.execute_assignment_test_plan(test_plan) def test_list_role_assignments_filtered_by_role(self): - """Test listing of role assignments filtered by role ID. - - Test Plan: - - - Create a user, group & project - - Find how many role assignments already exist (from default - fixtures) - - Create a grant of each type (user/group on project/domain) - - Check that if we list assignments by role_id, then we get back - assignments that only contain that role. - - """ - new_user = {'name': uuid.uuid4().hex, 'password': uuid.uuid4().hex, - 'enabled': True, 'domain_id': DEFAULT_DOMAIN_ID} - new_user = self.identity_api.create_user(new_user) - new_group = {'domain_id': DEFAULT_DOMAIN_ID, 'name': uuid.uuid4().hex} - new_group = self.identity_api.create_group(new_group) - new_project = {'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'domain_id': DEFAULT_DOMAIN_ID} - self.resource_api.create_project(new_project['id'], new_project) - - # First check how many role grants already exist - existing_assignments_for_role = len( - self.assignment_api.list_role_assignments_for_role( - role_id='admin')) - - # Now create the grants (roles are defined in default_fixtures) - self.assignment_api.create_grant(user_id=new_user['id'], - domain_id=DEFAULT_DOMAIN_ID, - role_id='member') - self.assignment_api.create_grant(user_id=new_user['id'], - project_id=new_project['id'], - role_id='other') - self.assignment_api.create_grant(group_id=new_group['id'], - domain_id=DEFAULT_DOMAIN_ID, - role_id='admin') - self.assignment_api.create_grant(group_id=new_group['id'], - project_id=new_project['id'], - role_id='admin') + """Test listing of role assignments filtered by role ID.""" + + test_plan = { + # Create a user, group & project in the default domain + 'entities': {'domains': {'id': DEFAULT_DOMAIN_ID, + 'users': 1, 'groups': 1, 'projects': 1}, + 'roles': 3}, + # Create a grant of each type (user/group on project/domain) + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}], + 'tests': [ + # Check that when filtering by role, we only get back those + # that match + {'params': {'role': 2}, + 'results': [{'group': 0, 'role': 2, 'domain': 0}, + {'group': 0, 'role': 2, 'project': 0}]} + ] + } + test_data = self.execute_assignment_test_plan(test_plan) - # Read back the list of assignments for just the admin role, checking - # this only goes up by two. + # Also test that list_role_assignments_for_role() gives the same answer assignment_list = self.assignment_api.list_role_assignments_for_role( - role_id='admin') - self.assertEqual(existing_assignments_for_role + 2, - len(assignment_list)) + role_id=test_data['roles'][2]['id']) + self.assertThat(assignment_list, matchers.HasLength(2)) # Now check that each of our two new entries are in the list self.assertIn( - {'group_id': new_group['id'], 'domain_id': DEFAULT_DOMAIN_ID, - 'role_id': 'admin'}, + {'group_id': test_data['groups'][0]['id'], + 'domain_id': DEFAULT_DOMAIN_ID, + 'role_id': test_data['roles'][2]['id']}, assignment_list) self.assertIn( - {'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': 'admin'}, + {'group_id': test_data['groups'][0]['id'], + 'project_id': test_data['projects'][0]['id'], + 'role_id': test_data['roles'][2]['id']}, assignment_list) def test_list_group_role_assignment(self): # When a group role assignment is created and the role assignments are # listed then the group role assignment is included in the list. - MEMBER_ROLE_ID = 'member' - - def get_member_assignments(): - assignments = self.assignment_api.list_role_assignments() - return [x for x in assignments if x['role_id'] == MEMBER_ROLE_ID] - - orig_member_assignments = get_member_assignments() - - # Create a group. - new_group = { - 'domain_id': DEFAULT_DOMAIN_ID, - 'name': self.getUniqueString(prefix='tlgra')} - new_group = self.identity_api.create_group(new_group) - - # Create a project. - new_project = { - 'id': uuid.uuid4().hex, - 'name': self.getUniqueString(prefix='tlgra'), - 'domain_id': DEFAULT_DOMAIN_ID} - self.resource_api.create_project(new_project['id'], new_project) - - # Assign a role to the group. - self.assignment_api.create_grant( - group_id=new_group['id'], project_id=new_project['id'], - role_id=MEMBER_ROLE_ID) - - # List role assignments - new_member_assignments = get_member_assignments() - - expected_member_assignments = orig_member_assignments + [{ - 'group_id': new_group['id'], 'project_id': new_project['id'], - 'role_id': MEMBER_ROLE_ID}] - self.assertItemsEqual(expected_member_assignments, - new_member_assignments) + test_plan = { + 'entities': {'domains': {'id': DEFAULT_DOMAIN_ID, + 'groups': 1, 'projects': 1}, + 'roles': 1}, + 'assignments': [{'group': 0, 'role': 0, 'project': 0}], + 'tests': [ + {'params': {}, + 'results': [{'group': 0, 'role': 0, 'project': 0}]} + ] + } + self.execute_assignment_test_plan(test_plan) def test_list_role_assignments_bad_role(self): assignment_list = self.assignment_api.list_role_assignments_for_role( @@ -1695,6 +1982,82 @@ class IdentityTests(object): self.identity_api.delete_group(group1['id']) self.identity_api.get_user(user1['id']) + def test_list_role_assignment_by_domain(self): + """Test listing of role assignment filtered by domain.""" + + test_plan = { + # A domain with 3 users, 1 group, a spoiler domain and 2 roles. + 'entities': {'domains': [{'users': 3, 'groups': 1}, 1], + 'roles': 2}, + # Users 1 & 2 are in the group + 'group_memberships': [{'group': 0, 'users': [1, 2]}], + # Assign a role for user 0 and the group + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}], + 'tests': [ + # List all effective assignments for domain[0]. + # Should get one direct user role and user roles for each of + # the users in the group. + {'params': {'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 1, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 2, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}} + ]}, + # Using domain[1] should return nothing + {'params': {'domain': 1, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_test_plan(test_plan) + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + """Test listing assignments by user, with group roles on a domain.""" + + test_plan = { + # A domain with 3 users, 3 groups, a spoiler domain + # plus 3 roles. + 'entities': {'domains': [{'users': 3, 'groups': 3}, 1], + 'roles': 3}, + # Users 1 & 2 are in the group 0, User 1 also in group 1 + 'group_memberships': [{'group': 0, 'users': [0, 1]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0}, + # ...and two spoiler assignments + {'user': 1, 'role': 1, 'domain': 0}, + {'group': 2, 'role': 2, 'domain': 0}], + 'tests': [ + # List all effective assignments for user[0]. + # Should get one direct user role and a user roles for each of + # groups 0 and 1 + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Adding domain[0] as a filter should return the same data + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'domain': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 2, 'domain': 0, + 'indirect': {'group': 1}} + ]}, + # Using domain[1] should return nothing + {'params': {'user': 0, 'domain': 1, 'effective': True}, + 'results': []}, + # Using user[2] should return nothing + {'params': {'user': 2, 'domain': 0, 'effective': True}, + 'results': []}, + ] + } + self.execute_assignment_test_plan(test_plan) + def test_delete_domain_with_user_group_project_links(self): # TODO(chungg):add test case once expected behaviour defined pass @@ -2162,7 +2525,7 @@ class IdentityTests(object): self.assertIn(self.tenant_mtu['id'], project_ids) self.assertIn(self.tenant_service['id'], project_ids) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_list_projects_for_alternate_domain(self): domain1 = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} self.resource_api.create_domain(domain1['id'], domain1) @@ -2219,7 +2582,7 @@ class IdentityTests(object): return projects - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_with_project_api(self): project_id = uuid.uuid4().hex project = {'id': project_id, @@ -2233,7 +2596,7 @@ class IdentityTests(object): self.assertTrue(ref['is_domain']) self.assertEqual(DEFAULT_DOMAIN_ID, ref['domain_id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_is_domain_sub_project_has_parent_domain_id(self): project = {'id': uuid.uuid4().hex, @@ -2258,7 +2621,7 @@ class IdentityTests(object): self.assertEqual(project['id'], ref['parent_id']) self.assertEqual(project['id'], ref['domain_id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_delete_domain_with_project_api(self): project_id = uuid.uuid4().hex @@ -2283,7 +2646,7 @@ class IdentityTests(object): # Successfuly delete the project self.resource_api.delete_project(project['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_create_domain_under_regular_project_hierarchy_fails(self): # Creating a regular project hierarchy. Projects acting as domains @@ -2303,7 +2666,7 @@ class IdentityTests(object): self.resource_api.create_project, project['id'], project) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support @test_utils.wip('waiting for projects acting as domains implementation') def test_create_project_under_domain_hierarchy(self): projects_hierarchy = self._create_projects_hierarchy(is_domain=True) @@ -2917,7 +3280,7 @@ class IdentityTests(object): uuid.uuid4().hex, DEFAULT_DOMAIN_ID) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_group_crud(self): group = {'domain_id': DEFAULT_DOMAIN_ID, 'name': uuid.uuid4().hex} group = self.identity_api.create_group(group) @@ -2992,7 +3355,7 @@ class IdentityTests(object): group1['id'], group1) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_project_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3093,7 +3456,7 @@ class IdentityTests(object): project['id'], project) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_leaf_project_with_different_domain(self): root_project = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, @@ -3308,7 +3671,7 @@ class IdentityTests(object): self.resource_api.get_domain, domain['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_case_sensitivity(self): # create a ref with a lowercase name ref = { @@ -3443,8 +3806,8 @@ class IdentityTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(3, len(user_projects)) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_domain_rename_invalidates_get_domain_by_name_cache(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3458,7 +3821,7 @@ class IdentityTests(object): self.resource_api.get_domain_by_name, domain_name) - @tests.skip_if_cache_disabled('resource') + @unit.skip_if_cache_disabled('resource') def test_cache_layer_domain_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3513,8 +3876,8 @@ class IdentityTests(object): self.resource_api.get_domain, domain_id) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_project_rename_invalidates_get_project_by_name_cache(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3533,8 +3896,8 @@ class IdentityTests(object): project_name, domain['id']) - @tests.skip_if_cache_disabled('resource') - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_cache_disabled('resource') + @unit.skip_if_no_multiple_domains_support def test_cache_layer_project_crud(self): domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, 'enabled': True} @@ -3646,7 +4009,7 @@ class IdentityTests(object): group_id=uuid.uuid4().hex, project_id=self.tenant_bar['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_get_default_domain_by_name(self): domain_name = 'default' @@ -4308,7 +4671,7 @@ class TokenTests(object): def test_list_revoked_tokens_for_multiple_tokens(self): self.check_list_revoked_tokens([self.delete_token() - for x in six.moves.range(2)]) + for x in range(2)]) def test_flush_expired_token(self): token_id = uuid.uuid4().hex @@ -4339,7 +4702,7 @@ class TokenTests(object): self.assertEqual(1, len(tokens)) self.assertIn(token_id, tokens) - @tests.skip_if_cache_disabled('token') + @unit.skip_if_cache_disabled('token') def test_revocation_list_cache(self): expire_time = timeutils.utcnow() + datetime.timedelta(minutes=10) token_id = uuid.uuid4().hex @@ -4769,7 +5132,7 @@ class CatalogTests(object): for region in regions: self.assertEqual(parent_id, region['parent_region_id']) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_region_crud(self): region_id = uuid.uuid4().hex new_region = { @@ -4797,7 +5160,7 @@ class CatalogTests(object): self.assertRaises(exception.RegionNotFound, self.catalog_api.get_region, region_id) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_region(self): region_id = uuid.uuid4().hex new_region = { @@ -4875,7 +5238,7 @@ class CatalogTests(object): region_two['id'], {'parent_region_id': region_four['id']}) - @mock.patch.object(core.Driver, + @mock.patch.object(core.CatalogDriverV8, "_ensure_no_circle_in_hierarchical_regions") def test_circular_regions_can_be_deleted(self, mock_ensure_on_circle): # turn off the enforcement so that cycles can be created for the test @@ -5003,7 +5366,7 @@ class CatalogTests(object): self.catalog_api.delete_service(unrelated_service1['id']) self.catalog_api.delete_service(unrelated_service2['id']) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_service_crud(self): service_id = uuid.uuid4().hex new_service = { @@ -5040,7 +5403,7 @@ class CatalogTests(object): self.catalog_api.get_service, service_id) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_service(self): service_id = uuid.uuid4().hex new_service = { @@ -5351,7 +5714,7 @@ class CatalogTests(object): endpoint_ids = [x['id'] for x in catalog[0]['endpoints']] self.assertEqual([enabled_endpoint_ref['id']], endpoint_ids) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_endpoint(self): service = { 'id': uuid.uuid4().hex, @@ -5468,7 +5831,71 @@ class PolicyTests(object): uuid.uuid4().hex) -class InheritanceTests(object): +class InheritanceTests(AssignmentTestHelperMixin): + + def test_role_assignments_user_domain_to_project_inheritance(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0] + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': 'projects'}]}, + # Now the effective ones - so the domain role should turn into + # a project role + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Narrow down to effective roles for user[0] and project[0] + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + self.execute_assignment_test_plan(test_plan) + + def test_inherited_role_assignments_excluded_if_os_inherit_false(self): + test_plan = { + 'entities': {'domains': {'users': 2, 'groups': 1, 'projects': 1}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'user': 1, 'role': 1, 'project': 0}, + {'group': 0, 'role': 3, 'project': 0}], + 'tests': [ + # List all direct assignments for user[0], since os-inherit is + # disabled, we should not see the inherited role + {'params': {'user': 0}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}]}, + # Same in effective mode - inherited roles should not be + # included or expanded...but the group role should now + # turn up as a user role, since group expansion is not + # part of os-inherit. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'domain': 0}, + {'user': 0, 'role': 1, 'project': 0}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'group': 0}}]}, + ] + } + self.config_fixture.config(group='os_inherit', enabled=False) + self.execute_assignment_test_plan(test_plan) def _test_crud_inherited_and_direct_assignment(self, **kwargs): """Tests inherited and direct assignments for the actor and target @@ -5616,6 +6043,42 @@ class InheritanceTests(object): self.assertEqual(1, len(combined_role_list)) self.assertIn(role_list[1]['id'], combined_role_list) + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # and get_roles_for_user_and_domain, which will, in a subsequent patch, + # be re-implemeted to simply call list_role_assignments (see blueprint + # remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once get_roles_for_user_and + # project/domain have been re-implemented then the manual tests above + # can be refactored to simply ensure it gives the same answers. + test_plan = { + # A domain with a user & project, plus 3 roles. + 'entities': {'domains': {'users': 1, 'projects': 1}, + 'roles': 3}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 0}, + {'user': 0, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and one inherited role. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0}}]}, + # Ensure effective mode on the domain does not list the + # inherited role on that domain + {'params': {'user': 0, 'domain': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + # Ensure non-inherited mode also only returns the non-inherited + # role on the domain + {'params': {'user': 0, 'domain': 0, 'inherited': False}, + 'results': [{'user': 0, 'role': 1, 'domain': 0}]}, + ] + } + self.execute_assignment_test_plan(test_plan) + def test_inherited_role_grants_for_group(self): """Test inherited group roles. @@ -5699,6 +6162,42 @@ class InheritanceTests(object): self.assertIn(role_list[2]['id'], combined_list) self.assertIn(role_list[3]['id'], combined_list) + # TODO(henry-nash): The test above uses get_roles_for_user_and_project + # which will, in a subsequent patch, be re-implemeted to simply call + # list_role_assignments (see blueprint remove-role-metadata). + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once + # get_roles_for_user_and_project has been re-implemented then the + # manual tests above can be refactored to simply ensure it gives + # the same answers. + test_plan = { + # A domain with a user and project, 2 groups, plus 4 roles. + 'entities': {'domains': {'users': 1, 'projects': 1, 'groups': 2}, + 'roles': 4}, + 'group_memberships': [{'group': 0, 'users': [0]}, + {'group': 1, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'domain': 0}, + {'group': 1, 'role': 2, 'domain': 0, + 'inherited_to_projects': True}, + {'group': 1, 'role': 3, 'domain': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] on project[0]. + # Should get one direct role and both inherited roles, but + # not the direct one on domain[0], even though user[0] is + # in group[0]. + {'params': {'user': 0, 'project': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 2, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}, + {'user': 0, 'role': 3, 'project': 0, + 'indirect': {'domain': 0, 'group': 1}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_grants(self): """Test inherited user roles. @@ -5738,6 +6237,37 @@ class InheritanceTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(3, len(user_projects)) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with 1 project, plus a second domain with 2 projects, + # as well as a user. Also, create 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'projects': 2}], + 'roles': 2}, + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get one direct role plus one inherited role for each + # project in domain + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_user_project_grants(self): """Test inherited role assignments for users on nested projects. @@ -5798,6 +6328,50 @@ class InheritanceTests(object): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project and sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + # A direct role and an inherited role on the parent + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + def test_list_projects_for_user_with_inherited_group_grants(self): """Test inherited group roles. @@ -5862,6 +6436,48 @@ class InheritanceTests(object): user_projects = self.assignment_api.list_projects_for_user(user1['id']) self.assertEqual(5, len(user_projects)) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a 1 project, plus a second domain with 2 projects, + # as well as a user & group and a 3rd domain with 2 projects. + # Also, created 2 roles. + 'entities': {'domains': [{'projects': 1}, + {'users': 1, 'groups': 1, 'projects': 2}, + {'projects': 2}], + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + 'assignments': [{'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 1, 'domain': 1, + 'inherited_to_projects': True}, + {'user': 0, 'role': 1, 'domain': 2, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] + # Should get back both direct roles plus roles on both projects + # from each domain. Duplicates should not be fitered out. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 3}, + {'user': 0, 'role': 0, 'project': 0}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 2, + 'indirect': {'domain': 1}}, + {'user': 0, 'role': 1, 'project': 3, + 'indirect': {'domain': 2}}, + {'user': 0, 'role': 1, 'project': 4, + 'indirect': {'domain': 2}}]} + ] + } + self.execute_assignment_test_plan(test_plan) + def test_list_projects_for_user_with_inherited_group_project_grants(self): """Test inherited role assignments for groups on nested projects. @@ -5925,6 +6541,53 @@ class InheritanceTests(object): self.assertEqual(1, len(user_projects)) self.assertIn(root_project, user_projects) + # TODO(henry-nash): The test above uses list_projects_for_user + # which may, in a subsequent patch, be re-implemeted to call + # list_role_assignments and then report only the distinct projects. + # + # The test plan below therefore mirrors this test, to ensure that + # list_role_assignments works the same. Once list_projects_for_user + # has been re-implemented then the manual tests above can be + # refactored. + test_plan = { + # A domain with a project ans sub-project, plus a user. + # Also, create 2 roles. + 'entities': { + 'domains': {'id': DEFAULT_DOMAIN_ID, 'users': 1, 'groups': 1, + 'projects': {'project': 1}}, + 'roles': 2}, + 'group_memberships': [{'group': 0, 'users': [0]}], + # A direct role and an inherited role on the parent + 'assignments': [{'group': 0, 'role': 0, 'project': 0}, + {'group': 0, 'role': 1, 'project': 0, + 'inherited_to_projects': True}], + 'tests': [ + # List all effective assignments for user[0] - should get back + # one direct role plus one inherited role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}, + {'user': 0, 'role': 1, 'project': 1, + 'indirect': {'group': 0, 'project': 0}}]} + ] + } + + test_plan_with_os_inherit_disabled = { + 'tests': [ + # List all effective assignments for user[0] - should only get + # back the one direct role. + {'params': {'user': 0, 'effective': True}, + 'results': [{'user': 0, 'role': 0, 'project': 0, + 'indirect': {'group': 0}}]} + ] + } + self.config_fixture.config(group='os_inherit', enabled=True) + test_data = self.execute_assignment_test_plan(test_plan) + self.config_fixture.config(group='os_inherit', enabled=False) + # Pass the existing test data in to allow execution of 2nd test plan + self.execute_assignment_tests( + test_plan_with_os_inherit_disabled, test_data) + class FilterTests(filtering.FilterTests): def test_list_entities_filtered(self): diff --git a/keystone-moon/keystone/tests/unit/test_backend_kvs.py b/keystone-moon/keystone/tests/unit/test_backend_kvs.py index a22faa59..7406192a 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_kvs.py +++ b/keystone-moon/keystone/tests/unit/test_backend_kvs.py @@ -20,14 +20,14 @@ import six from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend CONF = cfg.CONF -class KvsToken(tests.TestCase, test_backend.TokenTests): +class KvsToken(unit.TestCase, test_backend.TokenTests): def setUp(self): super(KvsToken, self).setUp() self.load_backends() @@ -103,7 +103,7 @@ class KvsToken(tests.TestCase, test_backend.TokenTests): self.assertEqual(expected_user_token_list, user_token_list) -class KvsCatalog(tests.TestCase, test_backend.CatalogTests): +class KvsCatalog(unit.TestCase, test_backend.CatalogTests): def setUp(self): super(KvsCatalog, self).setUp() self.load_backends() @@ -157,7 +157,7 @@ class KvsCatalog(tests.TestCase, test_backend.CatalogTests): self.skipTest("kvs backend doesn't support filtering") -class KvsTokenCacheInvalidation(tests.TestCase, +class KvsTokenCacheInvalidation(unit.TestCase, test_backend.TokenCacheInvalidation): def setUp(self): super(KvsTokenCacheInvalidation, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_backend_ldap.py b/keystone-moon/keystone/tests/unit/test_backend_ldap.py index 94fb82e7..808922a7 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_ldap.py +++ b/keystone-moon/keystone/tests/unit/test_backend_ldap.py @@ -21,6 +21,7 @@ import ldap import mock from oslo_config import cfg import pkg_resources +from six.moves import http_client from six.moves import range from testtools import matchers @@ -31,7 +32,7 @@ from keystone import exception from keystone import identity from keystone.identity.mapping_backends import mapping as map from keystone import resource -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit import identity_mapping as mapping_sql from keystone.tests.unit.ksfixtures import database @@ -138,7 +139,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests): def config_files(self): config_files = super(BaseLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def get_user_enabled_vals(self, user): @@ -712,7 +713,7 @@ class BaseLDAPIdentity(test_backend.IdentityTests): self.identity_api.get_group, group['id']) - @tests.skip_if_cache_disabled('identity') + @unit.skip_if_cache_disabled('identity') def test_cache_layer_group_crud(self): group = { 'domain_id': CONF.identity.default_domain_id, @@ -979,8 +980,21 @@ class BaseLDAPIdentity(test_backend.IdentityTests): # returned as part of the ref. self.assertIs(True, project_info['enabled']) + def test_list_role_assignment_by_domain(self): + """Multiple domain assignments are not supported.""" + self.assertRaises( + (exception.Forbidden, exception.DomainNotFound), + super(BaseLDAPIdentity, self).test_list_role_assignment_by_domain) + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + """Multiple domain assignments are not supported.""" + self.assertRaises( + (exception.Forbidden, exception.DomainNotFound), + super(BaseLDAPIdentity, self). + test_list_role_assignment_by_user_with_domain_group_roles) + -class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): +class LDAPIdentity(BaseLDAPIdentity, unit.TestCase): def setUp(self): # NOTE(dstanek): The database must be setup prior to calling the @@ -1578,7 +1592,7 @@ class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): self.resource_api.get_domain, domain['id']) - @tests.skip_if_no_multiple_domains_support + @unit.skip_if_no_multiple_domains_support def test_create_domain_case_sensitivity(self): # domains are read-only, so case sensitivity isn't an issue ref = { @@ -1633,7 +1647,7 @@ class LDAPIdentity(BaseLDAPIdentity, tests.TestCase): self.resource_api.get_project, project['id']) - @tests.skip_if_cache_disabled('assignment') + @unit.skip_if_cache_disabled('assignment') def test_cache_layer_project_crud(self): # NOTE(morganfainberg): LDAP implementation does not currently support # updating project names. This method override provides a different @@ -2091,7 +2105,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): def config_files(self): config_files = super(LDAPIdentityEnabledEmulation, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def config_overrides(self): @@ -2250,12 +2264,12 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): self.assertIs(False, user_ref['enabled']) -class LdapIdentitySqlAssignment(BaseLDAPIdentity, tests.SQLDriverOverrides, - tests.TestCase): +class LdapIdentitySqlAssignment(BaseLDAPIdentity, unit.SQLDriverOverrides, + unit.TestCase): def config_files(self): config_files = super(LdapIdentitySqlAssignment, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_sql.conf')) return config_files def setUp(self): @@ -2473,7 +2487,7 @@ class BaseMultiLDAPandSQLIdentity(object): self.identity_api._get_domain_driver_and_entity_id( user['id'])) - if expected_status == 200: + if expected_status == http_client.OK: ref = driver.get_user(entity_id) ref = self.identity_api._set_domain_id_and_mapping( ref, domain_id, driver, map.EntityType.USER) @@ -2518,8 +2532,8 @@ class BaseMultiLDAPandSQLIdentity(object): password=self.users[user]['password']) -class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, - tests.TestCase, BaseMultiLDAPandSQLIdentity): +class MultiLDAPandSQLIdentity(BaseLDAPIdentity, unit.SQLDriverOverrides, + unit.TestCase, BaseMultiLDAPandSQLIdentity): """Class to test common SQL plus individual LDAP backends. We define a set of domains and domain-specific backends: @@ -2598,7 +2612,7 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, """ self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, - domain_config_dir=tests.TESTCONF + '/domain_configs_multi_ldap') + domain_config_dir=unit.TESTCONF + '/domain_configs_multi_ldap') self.config_fixture.config(group='identity_mapping', backward_compatible_ids=False) @@ -2647,21 +2661,23 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, check_user = self.check_user check_user(self.users['user0'], - self.domains['domain_default']['id'], 200) + self.domains['domain_default']['id'], http_client.OK) for domain in [self.domains['domain1']['id'], self.domains['domain2']['id'], self.domains['domain3']['id'], self.domains['domain4']['id']]: check_user(self.users['user0'], domain, exception.UserNotFound) - check_user(self.users['user1'], self.domains['domain1']['id'], 200) + check_user(self.users['user1'], self.domains['domain1']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain2']['id'], self.domains['domain3']['id'], self.domains['domain4']['id']]: check_user(self.users['user1'], domain, exception.UserNotFound) - check_user(self.users['user2'], self.domains['domain2']['id'], 200) + check_user(self.users['user2'], self.domains['domain2']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain1']['id'], self.domains['domain3']['id'], @@ -2671,10 +2687,14 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, # domain3 and domain4 share the same backend, so you should be # able to see user3 and user4 from either. - check_user(self.users['user3'], self.domains['domain3']['id'], 200) - check_user(self.users['user3'], self.domains['domain4']['id'], 200) - check_user(self.users['user4'], self.domains['domain3']['id'], 200) - check_user(self.users['user4'], self.domains['domain4']['id'], 200) + check_user(self.users['user3'], self.domains['domain3']['id'], + http_client.OK) + check_user(self.users['user3'], self.domains['domain4']['id'], + http_client.OK) + check_user(self.users['user4'], self.domains['domain3']['id'], + http_client.OK) + check_user(self.users['user4'], self.domains['domain4']['id'], + http_client.OK) for domain in [self.domains['domain_default']['id'], self.domains['domain1']['id'], @@ -2803,6 +2823,17 @@ class MultiLDAPandSQLIdentity(BaseLDAPIdentity, tests.SQLDriverOverrides, base = super(BaseLDAPIdentity, self) base.test_list_role_assignments_filtered_by_role() + def test_list_role_assignment_by_domain(self): + # With multi LDAP this method should work, so override the override + # from BaseLDAPIdentity + super(BaseLDAPIdentity, self).test_list_role_assignment_by_domain + + def test_list_role_assignment_by_user_with_domain_group_roles(self): + # With multi LDAP this method should work, so override the override + # from BaseLDAPIdentity + super(BaseLDAPIdentity, self).\ + test_list_role_assignment_by_user_with_domain_group_roles + class MultiLDAPandSQLIdentityDomainConfigsInSQL(MultiLDAPandSQLIdentity): """Class to test the use of domain configs stored in the database. @@ -2919,19 +2950,104 @@ class MultiLDAPandSQLIdentityDomainConfigsInSQL(MultiLDAPandSQLIdentity): domain_cfgs.get_domain_conf(CONF.identity.default_domain_id)) self.assertEqual(CONF.ldap.url, default_config.ldap.url) - def test_setting_sql_driver_raises_exception(self): - """Ensure setting of domain specific sql driver is prevented.""" + def test_setting_multiple_sql_driver_raises_exception(self): + """Ensure setting multiple domain specific sql drivers is prevented.""" new_config = {'identity': {'driver': 'sql'}} self.domain_config_api.create_config( CONF.identity.default_domain_id, new_config) - self.assertRaises(exception.InvalidDomainConfig, + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, self.identity_api.domain_configs.get_domain_conf, - CONF.identity.default_domain_id) + self.domains['domain1']['id']) + + def test_same_domain_gets_sql_driver(self): + """Ensure we can set an SQL driver if we have had it before.""" + + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config( + CONF.identity.default_domain_id, new_config) + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + + # By using a slightly different config, we cause the driver to be + # reloaded...and hence check if we can reuse the sql driver + new_config = {'identity': {'driver': 'sql'}, + 'ldap': {'url': 'fake://memory1'}} + self.domain_config_api.create_config( + CONF.identity.default_domain_id, new_config) + self.identity_api.domain_configs.get_domain_conf( + CONF.identity.default_domain_id) + + def test_delete_domain_clears_sql_registration(self): + """Ensure registration is deleted when a domain is deleted.""" + + domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + domain = self.resource_api.create_domain(domain['id'], domain) + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config(domain['id'], new_config) + self.identity_api.domain_configs.get_domain_conf(domain['id']) + + # First show that trying to set SQL for another driver fails + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, + self.identity_api.domain_configs.get_domain_conf, + self.domains['domain1']['id']) + self.domain_config_api.delete_config(self.domains['domain1']['id']) + + # Now we delete the domain + domain['enabled'] = False + self.resource_api.update_domain(domain['id'], domain) + self.resource_api.delete_domain(domain['id']) + + # The registration should now be available + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.identity_api.domain_configs.get_domain_conf( + self.domains['domain1']['id']) + + def test_orphaned_registration_does_not_prevent_getting_sql_driver(self): + """Ensure we self heal an orphaned sql registration.""" + + domain = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex} + domain = self.resource_api.create_domain(domain['id'], domain) + new_config = {'identity': {'driver': 'sql'}} + self.domain_config_api.create_config(domain['id'], new_config) + self.identity_api.domain_configs.get_domain_conf(domain['id']) + + # First show that trying to set SQL for another driver fails + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.assertRaises(exception.MultipleSQLDriversInConfig, + self.identity_api.domain_configs.get_domain_conf, + self.domains['domain1']['id']) + + # Now we delete the domain by using the backend driver directly, + # which causes the domain to be deleted without any of the cleanup + # that is in the manager (this is simulating a server process crashing + # in the middle of a delete domain operation, and somehow leaving the + # domain config settings in place, but the domain is deleted). We + # should still be able to set another domain to SQL, since we should + # self heal this issue. + + self.resource_api.driver.delete_domain(domain['id']) + # Invalidate cache (so we will see the domain has gone) + self.resource_api.get_domain.invalidate( + self.resource_api, domain['id']) + + # The registration should now be available + self.domain_config_api.create_config(self.domains['domain1']['id'], + new_config) + self.identity_api.domain_configs.get_domain_conf( + self.domains['domain1']['id']) class DomainSpecificLDAPandSQLIdentity( - BaseLDAPIdentity, tests.SQLDriverOverrides, tests.TestCase, + BaseLDAPIdentity, unit.SQLDriverOverrides, unit.TestCase, BaseMultiLDAPandSQLIdentity): """Class to test when all domains use specific configs, including SQL. @@ -2954,7 +3070,7 @@ class DomainSpecificLDAPandSQLIdentity( self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, domain_config_dir=( - tests.TESTCONF + '/domain_configs_one_sql_one_ldap')) + unit.TESTCONF + '/domain_configs_one_sql_one_ldap')) self.config_fixture.config(group='identity_mapping', backward_compatible_ids=False) @@ -3035,12 +3151,12 @@ class DomainSpecificLDAPandSQLIdentity( # driver, but won't find it via any other domain driver self.check_user(self.users['user0'], - self.domains['domain_default']['id'], 200) + self.domains['domain_default']['id'], http_client.OK) self.check_user(self.users['user0'], self.domains['domain1']['id'], exception.UserNotFound) self.check_user(self.users['user1'], - self.domains['domain1']['id'], 200) + self.domains['domain1']['id'], http_client.OK) self.check_user(self.users['user1'], self.domains['domain_default']['id'], exception.UserNotFound) @@ -3113,7 +3229,7 @@ class DomainSpecificSQLIdentity(DomainSpecificLDAPandSQLIdentity): self.config_fixture.config( group='identity', domain_specific_drivers_enabled=True, domain_config_dir=( - tests.TESTCONF + '/domain_configs_default_ldap_one_sql')) + unit.TESTCONF + '/domain_configs_default_ldap_one_sql')) # Part of the testing counts how many new mappings get created as # we create users, so ensure we are NOT using mapping for the default # LDAP domain so this doesn't confuse the calculation. @@ -3189,12 +3305,12 @@ class DomainSpecificSQLIdentity(DomainSpecificLDAPandSQLIdentity): exception.MultipleSQLDriversInConfig, self.identity_api.domain_configs._load_config_from_file, self.resource_api, - [tests.TESTCONF + '/domain_configs_one_extra_sql/' + + [unit.TESTCONF + '/domain_configs_one_extra_sql/' + 'keystone.domain2.conf'], 'domain2') -class LdapFilterTests(test_backend.FilterTests, tests.TestCase): +class LdapFilterTests(test_backend.FilterTests, unit.TestCase): def setUp(self): super(LdapFilterTests, self).setUp() @@ -3212,7 +3328,7 @@ class LdapFilterTests(test_backend.FilterTests, tests.TestCase): def config_files(self): config_files = super(LdapFilterTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap.conf')) return config_files def test_list_users_in_group_filtered(self): diff --git a/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py b/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py index 66827d7e..2b714b57 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py +++ b/keystone-moon/keystone/tests/unit/test_backend_ldap_pool.py @@ -21,7 +21,7 @@ from oslotest import mockpatch from keystone.common.ldap import core as ldap_core from keystone.identity.backends import ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import fakeldap from keystone.tests.unit import test_backend_ldap @@ -209,7 +209,7 @@ class LdapPoolCommonTestMixin(object): class LdapIdentitySqlAssignment(LdapPoolCommonTestMixin, test_backend_ldap.LdapIdentitySqlAssignment, - tests.TestCase): + unit.TestCase): """Executes tests in existing base class with pooled LDAP handler.""" def setUp(self): self.useFixture(mockpatch.PatchObject( @@ -226,7 +226,7 @@ class LdapIdentitySqlAssignment(LdapPoolCommonTestMixin, def config_files(self): config_files = super(LdapIdentitySqlAssignment, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_ldap_pool.conf')) + config_files.append(unit.dirs.tests_conf('backend_ldap_pool.conf')) return config_files @mock.patch.object(ldap_core, 'utf8_encode') diff --git a/keystone-moon/keystone/tests/unit/test_backend_rules.py b/keystone-moon/keystone/tests/unit/test_backend_rules.py index bc0dc13d..9a11fddc 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_rules.py +++ b/keystone-moon/keystone/tests/unit/test_backend_rules.py @@ -14,11 +14,11 @@ from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend -class RulesPolicy(tests.TestCase, test_backend.PolicyTests): +class RulesPolicy(unit.TestCase, test_backend.PolicyTests): def setUp(self): super(RulesPolicy, self).setUp() self.load_backends() diff --git a/keystone-moon/keystone/tests/unit/test_backend_sql.py b/keystone-moon/keystone/tests/unit/test_backend_sql.py index bf50ac21..69fac63a 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_sql.py +++ b/keystone-moon/keystone/tests/unit/test_backend_sql.py @@ -29,7 +29,7 @@ from keystone.common import driver_hints from keystone.common import sql from keystone import exception from keystone.identity.backends import sql as identity_sql -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_backend @@ -40,7 +40,7 @@ CONF = cfg.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id -class SqlTests(tests.SQLDriverOverrides, tests.TestCase): +class SqlTests(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): super(SqlTests, self).setUp() @@ -54,7 +54,7 @@ class SqlTests(tests.SQLDriverOverrides, tests.TestCase): def config_files(self): config_files = super(SqlTests, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files @@ -840,7 +840,7 @@ class FakeTable(sql.ModelBase): raise KeyError -class SqlDecorators(tests.TestCase): +class SqlDecorators(unit.TestCase): def test_initialization_fail(self): self.assertRaises(exception.StringLengthExceeded, @@ -863,7 +863,7 @@ class SqlDecorators(tests.TestCase): self.assertRaises(KeyError, FakeTable().lookup) -class SqlModuleInitialization(tests.TestCase): +class SqlModuleInitialization(unit.TestCase): @mock.patch.object(sql.core, 'CONF') @mock.patch.object(options, 'set_defaults') diff --git a/keystone-moon/keystone/tests/unit/test_backend_templated.py b/keystone-moon/keystone/tests/unit/test_backend_templated.py index 82a8bed8..4a7bf9e5 100644 --- a/keystone-moon/keystone/tests/unit/test_backend_templated.py +++ b/keystone-moon/keystone/tests/unit/test_backend_templated.py @@ -18,7 +18,7 @@ import mock from six.moves import zip from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_backend @@ -28,7 +28,7 @@ BROKEN_WRITE_FUNCTIONALITY_MSG = ("Templated backend doesn't correctly " "implement write operations") -class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): +class TestTemplatedCatalog(unit.TestCase, test_backend.CatalogTests): DEFAULT_FIXTURE = { 'RegionOne': { @@ -60,7 +60,7 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): self.config_fixture.config( group='catalog', driver='templated', - template_file=tests.dirs.tests('default_catalog.templates')) + template_file=unit.dirs.tests('default_catalog.templates')) def test_get_catalog(self): catalog_ref = self.catalog_api.get_catalog('foo', 'bar') @@ -161,11 +161,11 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): def test_region_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_region_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_region(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) @@ -189,11 +189,11 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): def test_service_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_cache_layer_service_crud(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_service(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) @@ -234,6 +234,6 @@ class TestTemplatedCatalog(tests.TestCase, test_backend.CatalogTests): endpoints = self.catalog_api.list_endpoints() self.assertEqual(expected_ids, set(e['id'] for e in endpoints)) - @tests.skip_if_cache_disabled('catalog') + @unit.skip_if_cache_disabled('catalog') def test_invalidate_cache_when_updating_endpoint(self): self.skipTest(BROKEN_WRITE_FUNCTIONALITY_MSG) diff --git a/keystone-moon/keystone/tests/unit/test_cache.py b/keystone-moon/keystone/tests/unit/test_cache.py index c60df877..3c2afe66 100644 --- a/keystone-moon/keystone/tests/unit/test_cache.py +++ b/keystone-moon/keystone/tests/unit/test_cache.py @@ -23,7 +23,7 @@ from oslo_config import cfg from keystone.common import cache from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF @@ -76,7 +76,7 @@ class TestProxyValue(object): self.cached = False -class CacheRegionTest(tests.TestCase): +class CacheRegionTest(unit.TestCase): def setUp(self): super(CacheRegionTest, self).setUp() @@ -293,7 +293,7 @@ class CacheRegionTest(tests.TestCase): "bogus") -class CacheNoopBackendTest(tests.TestCase): +class CacheNoopBackendTest(unit.TestCase): def setUp(self): super(CacheNoopBackendTest, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py b/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py index 369570d6..66f80c21 100644 --- a/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py +++ b/keystone-moon/keystone/tests/unit/test_cache_backend_mongo.py @@ -24,7 +24,7 @@ from six.moves import range from keystone.common.cache.backends import mongo from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit # Mock database structure sample where 'ks_cache' is database and @@ -278,7 +278,7 @@ class MyTransformer(mongo.BaseTransform): return super(MyTransformer, self).transform_outgoing(son, collection) -class MongoCache(tests.BaseTestCase): +class MongoCache(unit.BaseTestCase): def setUp(self): super(MongoCache, self).setUp() global COLLECTIONS diff --git a/keystone-moon/keystone/tests/unit/test_catalog.py b/keystone-moon/keystone/tests/unit/test_catalog.py index 4e7f4037..85acfedf 100644 --- a/keystone-moon/keystone/tests/unit/test_catalog.py +++ b/keystone-moon/keystone/tests/unit/test_catalog.py @@ -14,8 +14,10 @@ import uuid +from six.moves import http_client + from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import rest @@ -30,7 +32,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): self.useFixture(database.Database()) self.service_id = uuid.uuid4().hex - self.service = self.new_service_ref() + self.service = unit.new_service_ref() self.service['id'] = self.service_id self.catalog_api.create_service( self.service_id, @@ -47,24 +49,12 @@ class V2CatalogTestCase(rest.RestfulTestCase): super(V2CatalogTestCase, self).config_overrides() self.config_fixture.config(group='catalog', driver='sql') - def new_ref(self): - """Populates a ref with attributes common to all API entities.""" - return { - 'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'description': uuid.uuid4().hex, - 'enabled': True} - - def new_service_ref(self): - ref = self.new_ref() - ref['type'] = uuid.uuid4().hex - return ref - def _get_token_id(self, r): """Applicable only to JSON.""" return r.result['access']['token']['id'] - def _endpoint_create(self, expected_status=200, service_id=SERVICE_FIXTURE, + def _endpoint_create(self, expected_status=http_client.OK, + service_id=SERVICE_FIXTURE, publicurl='http://localhost:8080', internalurl='http://localhost:8080', adminurl='http://localhost:8080'): @@ -115,16 +105,20 @@ class V2CatalogTestCase(rest.RestfulTestCase): self.assertNotIn("internalurl", response.result['endpoint']) def test_endpoint_create_with_null_publicurl(self): - self._endpoint_create(expected_status=400, publicurl=None) + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + publicurl=None) def test_endpoint_create_with_empty_publicurl(self): - self._endpoint_create(expected_status=400, publicurl='') + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + publicurl='') def test_endpoint_create_with_null_service_id(self): - self._endpoint_create(expected_status=400, service_id=None) + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + service_id=None) def test_endpoint_create_with_empty_service_id(self): - self._endpoint_create(expected_status=400, service_id='') + self._endpoint_create(expected_status=http_client.BAD_REQUEST, + service_id='') def test_endpoint_create_with_valid_url(self): """Create endpoint with valid URL should be tested, too.""" @@ -132,7 +126,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): valid_url = 'http://127.0.0.1:8774/v1.1/$(tenant_id)s' # baseline tests that all valid URLs works - self._endpoint_create(expected_status=200, + self._endpoint_create(expected_status=http_client.OK, publicurl=valid_url, internalurl=valid_url, adminurl=valid_url) @@ -159,7 +153,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case one: publicurl, internalurl and adminurl are # all invalid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=invalid_url, adminurl=invalid_url) @@ -167,7 +161,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case two: publicurl, internalurl are invalid # and adminurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=invalid_url, adminurl=valid_url) @@ -175,7 +169,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case three: publicurl, adminurl are invalid # and internalurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=valid_url, adminurl=invalid_url) @@ -183,7 +177,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case four: internalurl, adminurl are invalid # and publicurl is valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=invalid_url, adminurl=invalid_url) @@ -191,7 +185,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case five: publicurl is invalid, internalurl # and adminurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=invalid_url, internalurl=valid_url, adminurl=valid_url) @@ -199,7 +193,7 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case six: internalurl is invalid, publicurl # and adminurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=invalid_url, adminurl=valid_url) @@ -207,13 +201,13 @@ class V2CatalogTestCase(rest.RestfulTestCase): # Case seven: adminurl is invalid, publicurl # and internalurl are valid for invalid_url in invalid_urls: - self._endpoint_create(expected_status=400, + self._endpoint_create(expected_status=http_client.BAD_REQUEST, publicurl=valid_url, internalurl=valid_url, adminurl=invalid_url) -class TestV2CatalogAPISQL(tests.TestCase): +class TestV2CatalogAPISQL(unit.TestCase): def setUp(self): super(TestV2CatalogAPISQL, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_cert_setup.py b/keystone-moon/keystone/tests/unit/test_cert_setup.py index 3d300810..47a99810 100644 --- a/keystone-moon/keystone/tests/unit/test_cert_setup.py +++ b/keystone-moon/keystone/tests/unit/test_cert_setup.py @@ -17,18 +17,19 @@ import os import shutil import mock +from six.moves import http_client from testtools import matchers from keystone.common import environment from keystone.common import openssl from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import rest from keystone import token -SSLDIR = tests.dirs.tmp('ssl') -CONF = tests.CONF +SSLDIR = unit.dirs.tmp('ssl') +CONF = unit.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id @@ -113,11 +114,13 @@ class CertSetupTestCase(rest.RestfulTestCase): # requests don't have some of the normal information signing_resp = self.request(self.public_app, '/v2.0/certificates/signing', - method='GET', expected_status=200) + method='GET', + expected_status=http_client.OK) cacert_resp = self.request(self.public_app, '/v2.0/certificates/ca', - method='GET', expected_status=200) + method='GET', + expected_status=http_client.OK) with open(CONF.signing.certfile) as f: self.assertEqual(f.read(), signing_resp.text) @@ -133,7 +136,7 @@ class CertSetupTestCase(rest.RestfulTestCase): for accept in [None, 'text/html', 'application/json', 'text/xml']: headers = {'Accept': accept} if accept else {} resp = self.request(self.public_app, path, method='GET', - expected_status=200, + expected_status=http_client.OK, headers=headers) self.assertEqual('text/html', resp.content_type) @@ -146,7 +149,7 @@ class CertSetupTestCase(rest.RestfulTestCase): def test_failure(self): for path in ['/v2.0/certificates/signing', '/v2.0/certificates/ca']: self.request(self.public_app, path, method='GET', - expected_status=500) + expected_status=http_client.INTERNAL_SERVER_ERROR) def test_pki_certs_rebuild(self): self.test_create_pki_certs() @@ -219,7 +222,7 @@ class CertSetupTestCase(rest.RestfulTestCase): self.assertEqual(cert_file1, cert_file2) -class TestExecCommand(tests.TestCase): +class TestExecCommand(unit.TestCase): @mock.patch.object(environment.subprocess.Popen, 'poll') def test_running_a_successful_command(self, mock_poll): diff --git a/keystone-moon/keystone/tests/unit/test_cli.py b/keystone-moon/keystone/tests/unit/test_cli.py index 3f37612e..d967eb53 100644 --- a/keystone-moon/keystone/tests/unit/test_cli.py +++ b/keystone-moon/keystone/tests/unit/test_cli.py @@ -23,17 +23,17 @@ from keystone.cmd import cli from keystone.common import dependency from keystone.i18n import _ from keystone import resource -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database CONF = cfg.CONF -class CliTestCase(tests.SQLDriverOverrides, tests.TestCase): +class CliTestCase(unit.SQLDriverOverrides, unit.TestCase): def config_files(self): config_files = super(CliTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def test_token_flush(self): @@ -42,7 +42,7 @@ class CliTestCase(tests.SQLDriverOverrides, tests.TestCase): cli.TokenFlush.main() -class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): +class CliDomainConfigAllTestCase(unit.SQLDriverOverrides, unit.TestCase): def setUp(self): self.useFixture(database.Database()) @@ -50,7 +50,7 @@ class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): self.load_backends() self.config_fixture.config( group='identity', - domain_config_dir=tests.TESTCONF + '/domain_configs_multi_ldap') + domain_config_dir=unit.TESTCONF + '/domain_configs_multi_ldap') self.domain_count = 3 self.setup_initial_domains() @@ -58,7 +58,7 @@ class CliDomainConfigAllTestCase(tests.SQLDriverOverrides, tests.TestCase): self.config_fixture.register_cli_opt(cli.command_opt) self.addCleanup(self.cleanup) config_files = super(CliDomainConfigAllTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def cleanup(self): diff --git a/keystone-moon/keystone/tests/unit/test_config.py b/keystone-moon/keystone/tests/unit/test_config.py index 431f9965..7984646d 100644 --- a/keystone-moon/keystone/tests/unit/test_config.py +++ b/keystone-moon/keystone/tests/unit/test_config.py @@ -18,31 +18,31 @@ from oslo_config import cfg from keystone import config from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit CONF = cfg.CONF -class ConfigTestCase(tests.TestCase): +class ConfigTestCase(unit.TestCase): def config_files(self): config_files = super(ConfigTestCase, self).config_files() # Insert the keystone sample as the first config file to be loaded # since it is used in one of the code paths to determine the paste-ini # location. - config_files.insert(0, tests.dirs.etc('keystone.conf.sample')) + config_files.insert(0, unit.dirs.etc('keystone.conf.sample')) return config_files def test_paste_config(self): - self.assertEqual(tests.dirs.etc('keystone-paste.ini'), + self.assertEqual(unit.dirs.etc('keystone-paste.ini'), config.find_paste_config()) self.config_fixture.config(group='paste_deploy', config_file=uuid.uuid4().hex) self.assertRaises(exception.ConfigFileNotFound, config.find_paste_config) self.config_fixture.config(group='paste_deploy', config_file='') - self.assertEqual(tests.dirs.etc('keystone.conf.sample'), + self.assertEqual(unit.dirs.etc('keystone.conf.sample'), config.find_paste_config()) def test_config_default(self): @@ -50,12 +50,12 @@ class ConfigTestCase(tests.TestCase): self.assertIs(None, CONF.auth.token) -class DeprecatedTestCase(tests.TestCase): +class DeprecatedTestCase(unit.TestCase): """Test using the original (deprecated) name for renamed options.""" def config_files(self): config_files = super(DeprecatedTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('deprecated.conf')) + config_files.append(unit.dirs.tests_conf('deprecated.conf')) return config_files def test_sql(self): @@ -66,12 +66,12 @@ class DeprecatedTestCase(tests.TestCase): self.assertEqual(54321, CONF.database.idle_timeout) -class DeprecatedOverrideTestCase(tests.TestCase): +class DeprecatedOverrideTestCase(unit.TestCase): """Test using the deprecated AND new name for renamed options.""" def config_files(self): config_files = super(DeprecatedOverrideTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('deprecated_override.conf')) + config_files.append(unit.dirs.tests_conf('deprecated_override.conf')) return config_files def test_sql(self): diff --git a/keystone-moon/keystone/tests/unit/test_contrib_ec2.py b/keystone-moon/keystone/tests/unit/test_contrib_ec2.py index c6717dc5..2810a47a 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_ec2.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_ec2.py @@ -18,12 +18,12 @@ from keystoneclient.contrib.ec2 import utils as ec2_utils from keystone.contrib.ec2 import controllers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database -class TestCredentialEc2(tests.TestCase): +class TestCredentialEc2(unit.TestCase): # TODO(davechen): more testcases for ec2 credential are expected here and # the file name would be renamed to "test_credential" to correspond with # "test_v3_credential.py". diff --git a/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py b/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py index 43ea1ac5..18c76dad 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_s3_core.py @@ -16,10 +16,10 @@ import uuid from keystone.contrib import s3 from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class S3ContribCore(tests.TestCase): +class S3ContribCore(unit.TestCase): def setUp(self): super(S3ContribCore, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py b/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py index 8664e2c3..b241b41b 100644 --- a/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py +++ b/keystone-moon/keystone/tests/unit/test_contrib_simple_cert.py @@ -12,6 +12,8 @@ import uuid +from six.moves import http_client + from keystone.tests.unit import test_v3 @@ -31,7 +33,7 @@ class TestSimpleCert(BaseTestCase): method='GET', path=path, headers={'Accept': content_type}, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(content_type, response.content_type.lower()) self.assertIn('---BEGIN', response.body) @@ -54,4 +56,4 @@ class TestSimpleCert(BaseTestCase): self.request(app=self.public_app, method='GET', path=path, - expected_status=500) + expected_status=http_client.INTERNAL_SERVER_ERROR) diff --git a/keystone-moon/keystone/tests/unit/test_exception.py b/keystone-moon/keystone/tests/unit/test_exception.py index bf541dfd..4d602ccc 100644 --- a/keystone-moon/keystone/tests/unit/test_exception.py +++ b/keystone-moon/keystone/tests/unit/test_exception.py @@ -21,10 +21,10 @@ import six from keystone.common import wsgi from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class ExceptionTestCase(tests.BaseTestCase): +class ExceptionTestCase(unit.BaseTestCase): def assertValidJsonRendering(self, e): resp = wsgi.render_exception(e) self.assertEqual(e.code, resp.status_int) diff --git a/keystone-moon/keystone/tests/unit/test_ipv6.py b/keystone-moon/keystone/tests/unit/test_ipv6.py index e3d467fb..df59429e 100644 --- a/keystone-moon/keystone/tests/unit/test_ipv6.py +++ b/keystone-moon/keystone/tests/unit/test_ipv6.py @@ -16,14 +16,14 @@ from oslo_config import cfg from keystone.common import environment -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import appserver CONF = cfg.CONF -class IPv6TestCase(tests.TestCase): +class IPv6TestCase(unit.TestCase): def setUp(self): self.skip_if_no_ipv6() diff --git a/keystone-moon/keystone/tests/unit/test_kvs.py b/keystone-moon/keystone/tests/unit/test_kvs.py index 77e05e6d..18931f5d 100644 --- a/keystone-moon/keystone/tests/unit/test_kvs.py +++ b/keystone-moon/keystone/tests/unit/test_kvs.py @@ -26,7 +26,7 @@ from keystone.common.kvs.backends import inmemdb from keystone.common.kvs.backends import memcached from keystone.common.kvs import core from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit NO_VALUE = api.NO_VALUE @@ -147,7 +147,7 @@ class TestMemcacheDriver(api.CacheBackend): self.client.set_multi(mapping, **self.set_arguments) -class KVSTest(tests.TestCase): +class KVSTest(unit.TestCase): def setUp(self): super(KVSTest, self).setUp() self.key_foo = 'foo_' + uuid.uuid4().hex @@ -569,7 +569,7 @@ class KVSTest(tests.TestCase): key=test_key) -class TestMemcachedBackend(tests.TestCase): +class TestMemcachedBackend(unit.TestCase): @mock.patch('keystone.common.kvs.backends.memcached._', six.text_type) def test_invalid_backend_fails_initialization(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_livetest.py index b9f56e8d..e2abd56d 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_livetest.py @@ -21,7 +21,7 @@ from six.moves import range from keystone import exception from keystone.identity.backends import ldap as identity_ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend_ldap @@ -78,7 +78,7 @@ class LiveLDAPIdentity(test_backend_ldap.LDAPIdentity): def config_files(self): config_files = super(LiveLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_liveldap.conf')) return config_files def test_build_tree(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py index a8776e5b..81e91ce5 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_pool_livetest.py @@ -19,7 +19,7 @@ from oslo_config import cfg from keystone.common.ldap import core as ldap_core from keystone.identity.backends import ldap -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import fakeldap from keystone.tests.unit import test_backend_ldap_pool from keystone.tests.unit import test_ldap_livetest @@ -44,8 +44,7 @@ class LiveLDAPPoolIdentity(test_backend_ldap_pool.LdapPoolCommonTestMixin, def config_files(self): config_files = super(LiveLDAPPoolIdentity, self).config_files() - config_files.append(tests.dirs. - tests_conf('backend_pool_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_pool_liveldap.conf')) return config_files def test_assert_connector_used_not_fake_ldap_pool(self): diff --git a/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py b/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py index e77bbc98..6b47bfd9 100644 --- a/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_ldap_tls_livetest.py @@ -18,7 +18,7 @@ from oslo_config import cfg from keystone import exception from keystone import identity -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_ldap_livetest @@ -40,7 +40,7 @@ class LiveTLSLDAPIdentity(test_ldap_livetest.LiveLDAPIdentity): def config_files(self): config_files = super(LiveTLSLDAPIdentity, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_tls_liveldap.conf')) + config_files.append(unit.dirs.tests_conf('backend_tls_liveldap.conf')) return config_files def test_tls_certfile_demand_option(self): diff --git a/keystone-moon/keystone/tests/unit/test_middleware.py b/keystone-moon/keystone/tests/unit/test_middleware.py index 3a26dd24..0eedb9c6 100644 --- a/keystone-moon/keystone/tests/unit/test_middleware.py +++ b/keystone-moon/keystone/tests/unit/test_middleware.py @@ -12,11 +12,21 @@ # License for the specific language governing permissions and limitations # under the License. +import hashlib +import uuid + from oslo_config import cfg +from six.moves import http_client import webob +from keystone.common import authorization +from keystone.common import tokenless_auth +from keystone.contrib.federation import constants as federation_constants +from keystone import exception from keystone import middleware -from keystone.tests import unit as tests +from keystone.tests import unit +from keystone.tests.unit import mapping_fixtures +from keystone.tests.unit import test_backend_sql CONF = cfg.CONF @@ -40,7 +50,7 @@ def make_response(**kwargs): return webob.Response(body) -class TokenAuthMiddlewareTest(tests.TestCase): +class TokenAuthMiddlewareTest(unit.TestCase): def test_request(self): req = make_request() req.headers[middleware.AUTH_TOKEN_HEADER] = 'MAGIC' @@ -49,7 +59,7 @@ class TokenAuthMiddlewareTest(tests.TestCase): self.assertEqual('MAGIC', context['token_id']) -class AdminTokenAuthMiddlewareTest(tests.TestCase): +class AdminTokenAuthMiddlewareTest(unit.TestCase): def test_request_admin(self): req = make_request() req.headers[middleware.AUTH_TOKEN_HEADER] = CONF.admin_token @@ -65,7 +75,7 @@ class AdminTokenAuthMiddlewareTest(tests.TestCase): self.assertFalse(context['is_admin']) -class PostParamsMiddlewareTest(tests.TestCase): +class PostParamsMiddlewareTest(unit.TestCase): def test_request_with_params(self): req = make_request(body="arg1=one", method='POST') middleware.PostParamsMiddleware(None).process_request(req) @@ -73,7 +83,7 @@ class PostParamsMiddlewareTest(tests.TestCase): self.assertEqual({"arg1": "one"}, params) -class JsonBodyMiddlewareTest(tests.TestCase): +class JsonBodyMiddlewareTest(unit.TestCase): def test_request_with_params(self): req = make_request(body='{"arg1": "one", "arg2": ["a"]}', content_type='application/json', @@ -87,14 +97,14 @@ class JsonBodyMiddlewareTest(tests.TestCase): content_type='application/json', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) def test_not_dict_body(self): req = make_request(body='42', content_type='application/json', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) self.assertTrue('valid JSON object' in resp.json['error']['message']) def test_no_content_type(self): @@ -109,7 +119,7 @@ class JsonBodyMiddlewareTest(tests.TestCase): content_type='text/plain', method='POST') resp = middleware.JsonBodyMiddleware(None).process_request(req) - self.assertEqual(400, resp.status_int) + self.assertEqual(http_client.BAD_REQUEST, resp.status_int) def test_unrecognized_content_type_without_body(self): req = make_request(content_type='text/plain', @@ -117,3 +127,624 @@ class JsonBodyMiddlewareTest(tests.TestCase): middleware.JsonBodyMiddleware(None).process_request(req) params = req.environ.get(middleware.PARAMS_ENV, {}) self.assertEqual({}, params) + + +class AuthContextMiddlewareTest(test_backend_sql.SqlTests): + + def setUp(self): + super(AuthContextMiddlewareTest, self).setUp() + self.client_issuer = uuid.uuid4().hex + self.untrusted_client_issuer = uuid.uuid4().hex + self.trusted_issuer = self.client_issuer + self.config_fixture.config(group='tokenless_auth', + trusted_issuer=[self.trusted_issuer]) + + # This idp_id is calculated based on + # sha256(self.client_issuer) + hashed_idp = hashlib.sha256(self.client_issuer) + self.idp_id = hashed_idp.hexdigest() + self._load_sample_data() + + def _load_sample_data(self): + self.domain_id = uuid.uuid4().hex + self.domain_name = uuid.uuid4().hex + self.project_id = uuid.uuid4().hex + self.project_name = uuid.uuid4().hex + self.user_name = uuid.uuid4().hex + self.user_password = uuid.uuid4().hex + self.user_email = uuid.uuid4().hex + self.protocol_id = 'x509' + self.role_id = uuid.uuid4().hex + self.role_name = uuid.uuid4().hex + # for ephemeral user + self.group_name = uuid.uuid4().hex + + # 1) Create a domain for the user. + self.domain = { + 'description': uuid.uuid4().hex, + 'enabled': True, + 'id': self.domain_id, + 'name': self.domain_name, + } + + self.resource_api.create_domain(self.domain_id, self.domain) + + # 2) Create a project for the user. + self.project = { + 'description': uuid.uuid4().hex, + 'domain_id': self.domain_id, + 'enabled': True, + 'id': self.project_id, + 'name': self.project_name, + } + + self.resource_api.create_project(self.project_id, self.project) + + # 3) Create a user in new domain. + self.user = { + 'name': self.user_name, + 'domain_id': self.domain_id, + 'project_id': self.project_id, + 'password': self.user_password, + 'email': self.user_email, + } + + self.user = self.identity_api.create_user(self.user) + + # Add IDP + self.idp = self._idp_ref(id=self.idp_id) + self.federation_api.create_idp(self.idp['id'], + self.idp) + + # Add a role + self.role = { + 'id': self.role_id, + 'name': self.role_name, + } + self.role_api.create_role(self.role_id, self.role) + + # Add a group + self.group = { + 'name': self.group_name, + 'domain_id': self.domain_id, + } + self.group = self.identity_api.create_group(self.group) + + # Assign a role to the user on a project + self.assignment_api.add_role_to_user_and_project( + user_id=self.user['id'], + tenant_id=self.project_id, + role_id=self.role_id) + + # Assign a role to the group on a project + self.assignment_api.create_grant( + role_id=self.role_id, + group_id=self.group['id'], + project_id=self.project_id) + + def _load_mapping_rules(self, rules): + # Add a mapping + self.mapping = self._mapping_ref(rules=rules) + self.federation_api.create_mapping(self.mapping['id'], + self.mapping) + # Add protocols + self.proto_x509 = self._proto_ref(mapping_id=self.mapping['id']) + self.proto_x509['id'] = self.protocol_id + self.federation_api.create_protocol(self.idp['id'], + self.proto_x509['id'], + self.proto_x509) + + def _idp_ref(self, id=None): + idp = { + 'id': id or uuid.uuid4().hex, + 'enabled': True, + 'description': uuid.uuid4().hex + } + return idp + + def _proto_ref(self, mapping_id=None): + proto = { + 'id': uuid.uuid4().hex, + 'mapping_id': mapping_id or uuid.uuid4().hex + } + return proto + + def _mapping_ref(self, rules=None): + if rules is None: + mapped_rules = {} + else: + mapped_rules = rules.get('rules', {}) + return { + 'id': uuid.uuid4().hex, + 'rules': mapped_rules + } + + def _assert_tokenless_auth_context(self, context, ephemeral_user=False): + self.assertIsNotNone(context) + self.assertEqual(self.project_id, context['project_id']) + self.assertIn(self.role_name, context['roles']) + if ephemeral_user: + self.assertEqual(self.group['id'], context['group_ids'][0]) + self.assertEqual('ephemeral', + context[federation_constants.PROTOCOL]) + self.assertEqual(self.idp_id, + context[federation_constants.IDENTITY_PROVIDER]) + else: + self.assertEqual(self.user['id'], context['user_id']) + + def _create_context(self, request, mapping_ref=None, + exception_expected=False): + """Builds the auth context from the given arguments. + + auth context will be returned from the AuthContextMiddleware based on + what is being passed in the given request and what mapping is being + setup in the backend DB. + + :param request: HTTP request + :param mapping_ref: A mapping in JSON structure will be setup in the + backend DB for mapping an user or a group. + :param exception_expected: Sets to True when an exception is expected + to raised based on the given arguments. + :returns: context an auth context contains user and role information + :rtype: dict + """ + if mapping_ref: + self._load_mapping_rules(mapping_ref) + + if not exception_expected: + (middleware.AuthContextMiddleware('Tokenless_auth_test'). + process_request(request)) + context = request.environ.get(authorization.AUTH_CONTEXT_ENV) + else: + context = middleware.AuthContextMiddleware('Tokenless_auth_test') + return context + + def test_context_already_exists(self): + req = make_request() + token_id = uuid.uuid4().hex + req.environ[authorization.AUTH_CONTEXT_ENV] = {'token_id': token_id} + context = self._create_context(request=req) + self.assertEqual(token_id, context['token_id']) + + def test_not_applicable_to_token_request(self): + env = {} + env['PATH_INFO'] = '/auth/tokens' + env['REQUEST_METHOD'] = 'POST' + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_no_tokenless_attributes_request(self): + req = make_request() + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_no_issuer_attribute_request(self): + env = {} + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_has_only_issuer_and_project_name_request(self): + env = {} + # SSL_CLIENT_I_DN is the attribute name that wsgi env + # references to issuer of the client certificate. + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_has_only_issuer_and_project_domain_name_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_DOMAIN_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_has_only_issuer_and_project_domain_id_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_DOMAIN_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_missing_both_domain_and_project_request(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + req = make_request(environ=env) + context = self._create_context(request=req, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_empty_trusted_issuer_list(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + trusted_issuer=[]) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_client_issuer_not_trusted(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.untrusted_client_issuer + env['HTTP_X_PROJECT_ID'] = uuid.uuid4().hex + req = make_request(environ=env) + context = self._create_context(request=req) + self.assertIsNone(context) + + def test_proj_scope_with_proj_id_and_proj_dom_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + # SSL_CLIENT_USER_NAME and SSL_CLIENT_DOMAIN_NAME are the types + # defined in the mapping that will map to the user name and + # domain name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_id_only_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_and_proj_dom_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_and_proj_dom_name_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_proj_scope_with_proj_name_only_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_mapping_with_userid_and_domainid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_ID'] = self.user['id'] + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_AND_DOMAINID) + self._assert_tokenless_auth_context(context) + + def test_mapping_with_userid_and_domainname_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_ID'] = self.user['id'] + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_AND_DOMAINNAME) + self._assert_tokenless_auth_context(context) + + def test_mapping_with_username_and_domainid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID) + self._assert_tokenless_auth_context(context) + + def test_only_domain_name_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_DOMAINNAME_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_only_domain_id_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_DOMAINID_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_missing_domain_data_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_ONLY, + exception_expected=True) + self.assertRaises(exception.ValidationError, + context.process_request, + req) + + def test_userid_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_ID'] = self.user['id'] + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERID_ONLY) + self._assert_tokenless_auth_context(context) + + def test_domain_disable_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + self.domain['enabled'] = False + self.domain = self.resource_api.update_domain( + self.domain['id'], self.domain) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID, + exception_expected=True) + self.assertRaises(exception.Unauthorized, + context.process_request, + req) + + def test_user_disable_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + env['SSL_CLIENT_DOMAIN_ID'] = self.domain_id + req = make_request(environ=env) + self.user['enabled'] = False + self.user = self.identity_api.update_user(self.user['id'], self.user) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINID, + exception_expected=True) + self.assertRaises(AssertionError, + context.process_request, + req) + + def test_invalid_user_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_ID'] = self.project_id + env['HTTP_X_PROJECT_DOMAIN_ID'] = self.domain_id + env['SSL_CLIENT_USER_NAME'] = uuid.uuid4().hex + env['SSL_CLIENT_DOMAIN_NAME'] = self.domain_name + req = make_request(environ=env) + context = self._create_context( + request=req, + mapping_ref=mapping_fixtures.MAPPING_WITH_USERNAME_AND_DOMAINNAME, + exception_expected=True) + self.assertRaises(exception.UserNotFound, + context.process_request, + req) + + def test_ephemeral_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_with_default_user_type_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + # this mapping does not have the user type defined + # and it should defaults to 'ephemeral' which is + # the expected type for the test case. + mapping = mapping_fixtures.MAPPING_FOR_DEFAULT_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_any_user_success(self): + """Ephemeral user does not need a specified user + Keystone is not looking to match the user, but a corresponding group. + """ + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = uuid.uuid4().hex + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping) + self._assert_tokenless_auth_context(context, ephemeral_user=True) + + def test_ephemeral_invalid_scope_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = uuid.uuid4().hex + env['HTTP_X_PROJECT_DOMAIN_NAME'] = uuid.uuid4().hex + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = self.group['id'] + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.Unauthorized, + context.process_request, + req) + + def test_ephemeral_no_group_found_fail(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + self.config_fixture.config(group='tokenless_auth', + protocol='ephemeral') + self.protocol_id = 'ephemeral' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = uuid.uuid4().hex + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.MappedGroupNotFound, + context.process_request, + req) + + def test_ephemeral_incorrect_mapping_fail(self): + """Ephemeral user picks up the non-ephemeral user mapping. + Looking up the mapping with protocol Id 'x509' will load up + the non-ephemeral user mapping, results unauthenticated. + """ + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + env['HTTP_X_PROJECT_NAME'] = self.project_name + env['HTTP_X_PROJECT_DOMAIN_NAME'] = self.domain_name + env['SSL_CLIENT_USER_NAME'] = self.user_name + req = make_request(environ=env) + # This will pick up the incorrect mapping + self.config_fixture.config(group='tokenless_auth', + protocol='x509') + self.protocol_id = 'x509' + mapping = mapping_fixtures.MAPPING_FOR_EPHEMERAL_USER.copy() + mapping['rules'][0]['local'][0]['group']['id'] = uuid.uuid4().hex + context = self._create_context( + request=req, + mapping_ref=mapping, + exception_expected=True) + self.assertRaises(exception.MappedGroupNotFound, + context.process_request, + req) + + def test_create_idp_id_success(self): + env = {} + env['SSL_CLIENT_I_DN'] = self.client_issuer + auth = tokenless_auth.TokenlessAuthHelper(env) + idp_id = auth._build_idp_id() + self.assertEqual(self.idp_id, idp_id) + + def test_create_idp_id_attri_not_found_fail(self): + env = {} + env[uuid.uuid4().hex] = self.client_issuer + auth = tokenless_auth.TokenlessAuthHelper(env) + expected_msg = ('Could not determine Identity Provider ID. The ' + 'configuration option %s was not found in the ' + 'request environment.' % + CONF.tokenless_auth.issuer_attribute) + # Check the content of the exception message as well + self.assertRaisesRegexp(exception.TokenlessAuthConfigError, + expected_msg, + auth._build_idp_id) diff --git a/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py b/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py index 9f67fbd7..bf60cff0 100644 --- a/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py +++ b/keystone-moon/keystone/tests/unit/test_no_admin_token_auth.py @@ -14,12 +14,13 @@ import os +from six.moves import http_client import webtest -from keystone.tests import unit as tests +from keystone.tests import unit -class TestNoAdminTokenAuth(tests.TestCase): +class TestNoAdminTokenAuth(unit.TestCase): def setUp(self): super(TestNoAdminTokenAuth, self).setUp() self.load_backends() @@ -27,7 +28,7 @@ class TestNoAdminTokenAuth(tests.TestCase): self._generate_paste_config() self.admin_app = webtest.TestApp( - self.loadapp(tests.dirs.tmp('no_admin_token_auth'), name='admin'), + self.loadapp(unit.dirs.tmp('no_admin_token_auth'), name='admin'), extra_environ=dict(REMOTE_ADDR='127.0.0.1')) self.addCleanup(setattr, self, 'admin_app', None) @@ -35,12 +36,12 @@ class TestNoAdminTokenAuth(tests.TestCase): # Generate a file, based on keystone-paste.ini, that doesn't include # admin_token_auth in the pipeline - with open(tests.dirs.etc('keystone-paste.ini'), 'r') as f: + with open(unit.dirs.etc('keystone-paste.ini'), 'r') as f: contents = f.read() new_contents = contents.replace(' admin_token_auth ', ' ') - filename = tests.dirs.tmp('no_admin_token_auth-paste.ini') + filename = unit.dirs.tmp('no_admin_token_auth-paste.ini') with open(filename, 'w') as f: f.write(new_contents) self.addCleanup(os.remove, filename) @@ -56,4 +57,4 @@ class TestNoAdminTokenAuth(tests.TestCase): # If the following does not raise, then the test is successful. self.admin_app.get(REQ_PATH, headers={'X-Auth-Token': 'NotAdminToken'}, - status=401) + status=http_client.UNAUTHORIZED) diff --git a/keystone-moon/keystone/tests/unit/test_policy.py b/keystone-moon/keystone/tests/unit/test_policy.py index 30df0b2b..b2f0e525 100644 --- a/keystone-moon/keystone/tests/unit/test_policy.py +++ b/keystone-moon/keystone/tests/unit/test_policy.py @@ -24,11 +24,11 @@ from testtools import matchers from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import temporaryfile -class BasePolicyTestCase(tests.TestCase): +class BasePolicyTestCase(unit.TestCase): def setUp(self): super(BasePolicyTestCase, self).setUp() rules.reset() @@ -214,15 +214,15 @@ class DefaultPolicyTestCase(BasePolicyTestCase): self.credentials, "example:noexist", {}) -class PolicyJsonTestCase(tests.TestCase): +class PolicyJsonTestCase(unit.TestCase): def _load_entries(self, filename): return set(json.load(open(filename))) def test_json_examples_have_matching_entries(self): - policy_keys = self._load_entries(tests.dirs.etc('policy.json')) + policy_keys = self._load_entries(unit.dirs.etc('policy.json')) cloud_policy_keys = self._load_entries( - tests.dirs.etc('policy.v3cloudsample.json')) + unit.dirs.etc('policy.v3cloudsample.json')) policy_extra_keys = ['admin_or_token_subject', 'service_admin_or_token_subject', @@ -236,7 +236,7 @@ class PolicyJsonTestCase(tests.TestCase): # All the targets in the sample policy file must be documented in # doc/source/policy_mapping.rst. - policy_keys = self._load_entries(tests.dirs.etc('policy.json')) + policy_keys = self._load_entries(unit.dirs.etc('policy.json')) # These keys are in the policy.json but aren't targets. policy_rule_keys = [ @@ -249,7 +249,7 @@ class PolicyJsonTestCase(tests.TestCase): # targets. doc_path = os.path.join( - tests.ROOTDIR, 'doc', 'source', 'policy_mapping.rst') + unit.ROOTDIR, 'doc', 'source', 'policy_mapping.rst') with open(doc_path) as doc_file: for line in doc_file: if line.startswith('Target'): diff --git a/keystone-moon/keystone/tests/unit/test_revoke.py b/keystone-moon/keystone/tests/unit/test_revoke.py index 5394688c..9062981f 100644 --- a/keystone-moon/keystone/tests/unit/test_revoke.py +++ b/keystone-moon/keystone/tests/unit/test_revoke.py @@ -22,7 +22,7 @@ from testtools import matchers from keystone.common import utils from keystone.contrib.revoke import model from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_backend_sql from keystone.token import provider @@ -191,7 +191,7 @@ class SqlRevokeTests(test_backend_sql.SqlTests, RevokeTests): revoke_by_id=False) -class KvsRevokeTests(tests.TestCase, RevokeTests): +class KvsRevokeTests(unit.TestCase, RevokeTests): def config_overrides(self): super(KvsRevokeTests, self).config_overrides() self.config_fixture.config(group='revoke', driver='kvs') @@ -205,7 +205,7 @@ class KvsRevokeTests(tests.TestCase, RevokeTests): self.load_backends() -class RevokeTreeTests(tests.TestCase): +class RevokeTreeTests(unit.TestCase): def setUp(self): super(RevokeTreeTests, self).setUp() self.events = [] diff --git a/keystone-moon/keystone/tests/unit/test_sql_livetest.py b/keystone-moon/keystone/tests/unit/test_sql_livetest.py index 96ee6c70..e2186907 100644 --- a/keystone-moon/keystone/tests/unit/test_sql_livetest.py +++ b/keystone-moon/keystone/tests/unit/test_sql_livetest.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_sql_migrate_extensions from keystone.tests.unit import test_sql_upgrade @@ -24,7 +24,7 @@ class PostgresqlMigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(PostgresqlMigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_postgresql.conf")) + files.append(unit.dirs.tests_conf("backend_postgresql.conf")) return files @@ -35,7 +35,7 @@ class MysqlMigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(MysqlMigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_mysql.conf")) + files.append(unit.dirs.tests_conf("backend_mysql.conf")) return files @@ -47,7 +47,7 @@ class PostgresqlRevokeExtensionsTests( def config_files(self): files = super(PostgresqlRevokeExtensionsTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_postgresql.conf")) + files.append(unit.dirs.tests_conf("backend_postgresql.conf")) return files @@ -58,7 +58,7 @@ class MysqlRevokeExtensionsTests(test_sql_migrate_extensions.RevokeExtension): def config_files(self): files = super(MysqlRevokeExtensionsTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_mysql.conf")) + files.append(unit.dirs.tests_conf("backend_mysql.conf")) return files @@ -69,5 +69,5 @@ class Db2MigrateTests(test_sql_upgrade.SqlUpgradeTests): def config_files(self): files = super(Db2MigrateTests, self).config_files() - files.append(tests.dirs.tests_conf("backend_db2.conf")) + files.append(unit.dirs.tests_conf("backend_db2.conf")) return files diff --git a/keystone-moon/keystone/tests/unit/test_sql_upgrade.py b/keystone-moon/keystone/tests/unit/test_sql_upgrade.py index 96dfa9e8..d617d445 100644 --- a/keystone-moon/keystone/tests/unit/test_sql_upgrade.py +++ b/keystone-moon/keystone/tests/unit/test_sql_upgrade.py @@ -48,7 +48,7 @@ from keystone.common.sql import migration_helpers from keystone.contrib import federation from keystone.contrib import revoke from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database @@ -124,14 +124,14 @@ EXTENSIONS = {'federation': federation, 'revoke': revoke} -class SqlMigrateBase(tests.SQLDriverOverrides, tests.TestCase): +class SqlMigrateBase(unit.SQLDriverOverrides, unit.TestCase): def initialize_sql(self): self.metadata = sqlalchemy.MetaData() self.metadata.bind = self.engine def config_files(self): config_files = super(SqlMigrateBase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def repo_package(self): @@ -141,15 +141,15 @@ class SqlMigrateBase(tests.SQLDriverOverrides, tests.TestCase): super(SqlMigrateBase, self).setUp() database.initialize_sql_session() conn_str = CONF.database.connection - if (conn_str != tests.IN_MEM_DB_CONN_STRING and + if (conn_str != unit.IN_MEM_DB_CONN_STRING and conn_str.startswith('sqlite') and - conn_str[10:] == tests.DEFAULT_TEST_DB_FILE): + conn_str[10:] == unit.DEFAULT_TEST_DB_FILE): # Override the default with a DB that is specific to the migration # tests only if the DB Connection string is the same as the global # default. This is required so that no conflicts occur due to the # global default DB already being under migrate control. This is # only needed if the DB is not-in-memory - db_file = tests.dirs.tmp('keystone_migrate_test.db') + db_file = unit.dirs.tmp('keystone_migrate_test.db') self.config_fixture.config( group='database', connection='sqlite:///%s' % db_file) @@ -636,6 +636,13 @@ class SqlUpgradeTests(SqlMigrateBase): 'enabled', 'domain_id', 'parent_id', 'is_domain']) + def test_add_config_registration(self): + config_registration = 'config_register' + self.upgrade(74) + self.assertTableDoesNotExist(config_registration) + self.upgrade(75) + self.assertTableColumns(config_registration, ['type', 'domain_id']) + def populate_user_table(self, with_pass_enab=False, with_pass_enab_domain=False): # Populate the appropriate fields in the user diff --git a/keystone-moon/keystone/tests/unit/test_ssl.py b/keystone-moon/keystone/tests/unit/test_ssl.py index 3b86bb2d..6a6d9ffb 100644 --- a/keystone-moon/keystone/tests/unit/test_ssl.py +++ b/keystone-moon/keystone/tests/unit/test_ssl.py @@ -19,21 +19,21 @@ import ssl from oslo_config import cfg from keystone.common import environment -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import appserver CONF = cfg.CONF -CERTDIR = tests.dirs.root('examples', 'pki', 'certs') -KEYDIR = tests.dirs.root('examples', 'pki', 'private') +CERTDIR = unit.dirs.root('examples', 'pki', 'certs') +KEYDIR = unit.dirs.root('examples', 'pki', 'private') CERT = os.path.join(CERTDIR, 'ssl_cert.pem') KEY = os.path.join(KEYDIR, 'ssl_key.pem') CA = os.path.join(CERTDIR, 'cacert.pem') CLIENT = os.path.join(CERTDIR, 'middleware.pem') -class SSLTestCase(tests.TestCase): +class SSLTestCase(unit.TestCase): def setUp(self): super(SSLTestCase, self).setUp() raise self.skipTest('SSL Version and Ciphers cannot be configured ' diff --git a/keystone-moon/keystone/tests/unit/test_token_bind.py b/keystone-moon/keystone/tests/unit/test_token_bind.py index 7dc7ccca..ee4d011a 100644 --- a/keystone-moon/keystone/tests/unit/test_token_bind.py +++ b/keystone-moon/keystone/tests/unit/test_token_bind.py @@ -18,7 +18,7 @@ import uuid from keystone.common import wsgi from keystone import exception from keystone.models import token_model -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_token_provider @@ -26,7 +26,7 @@ KERBEROS_BIND = 'USER@REALM' ANY = 'any' -class BindTest(tests.TestCase): +class BindTest(unit.TestCase): """Test binding tokens to a Principal. Even though everything in this file references kerberos the same concepts diff --git a/keystone-moon/keystone/tests/unit/test_token_provider.py b/keystone-moon/keystone/tests/unit/test_token_provider.py index 3ebb0187..f60f7d53 100644 --- a/keystone-moon/keystone/tests/unit/test_token_provider.py +++ b/keystone-moon/keystone/tests/unit/test_token_provider.py @@ -20,7 +20,7 @@ from oslo_utils import timeutils from keystone.common import dependency from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone import token from keystone.token.providers import fernet @@ -712,7 +712,7 @@ SAMPLE_MALFORMED_TOKEN = { } -class TestTokenProvider(tests.TestCase): +class TestTokenProvider(unit.TestCase): def setUp(self): super(TestTokenProvider, self).setUp() self.useFixture(database.Database()) @@ -817,7 +817,7 @@ class PKIProviderTests(object): token_data) -class TestPKIProviderWithEventlet(PKIProviderTests, tests.TestCase): +class TestPKIProviderWithEventlet(PKIProviderTests, unit.TestCase): def setUp(self): # force keystoneclient.common.cms to use eventlet's subprocess @@ -827,7 +827,7 @@ class TestPKIProviderWithEventlet(PKIProviderTests, tests.TestCase): super(TestPKIProviderWithEventlet, self).setUp() -class TestPKIProviderWithStdlib(PKIProviderTests, tests.TestCase): +class TestPKIProviderWithStdlib(PKIProviderTests, unit.TestCase): def setUp(self): # force keystoneclient.common.cms to use the stdlib subprocess diff --git a/keystone-moon/keystone/tests/unit/test_url_middleware.py b/keystone-moon/keystone/tests/unit/test_url_middleware.py index 1b3872b5..217b302d 100644 --- a/keystone-moon/keystone/tests/unit/test_url_middleware.py +++ b/keystone-moon/keystone/tests/unit/test_url_middleware.py @@ -15,7 +15,7 @@ import webob from keystone import middleware -from keystone.tests import unit as tests +from keystone.tests import unit class FakeApp(object): @@ -26,7 +26,7 @@ class FakeApp(object): return resp(env, start_response) -class UrlMiddlewareTest(tests.TestCase): +class UrlMiddlewareTest(unit.TestCase): def setUp(self): self.middleware = middleware.NormalizingFilter(FakeApp()) self.response_status = None diff --git a/keystone-moon/keystone/tests/unit/test_v2.py b/keystone-moon/keystone/tests/unit/test_v2.py index 415150cf..99b5a897 100644 --- a/keystone-moon/keystone/tests/unit/test_v2.py +++ b/keystone-moon/keystone/tests/unit/test_v2.py @@ -19,6 +19,7 @@ import uuid from keystoneclient.common import cms from oslo_config import cfg import six +from six.moves import http_client from testtools import matchers from keystone.common import extension as keystone_extension @@ -70,13 +71,13 @@ class CoreApiTests(object): def test_public_not_found(self): r = self.public_request( path='/%s' % uuid.uuid4().hex, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.assertValidErrorResponse(r) def test_admin_not_found(self): r = self.admin_request( path='/%s' % uuid.uuid4().hex, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.assertValidErrorResponse(r) def test_public_multiple_choice(self): @@ -107,11 +108,11 @@ class CoreApiTests(object): def test_admin_extensions_404(self): self.admin_request(path='/v2.0/extensions/invalid-extension', - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_public_osksadm_extension_404(self): self.public_request(path='/v2.0/extensions/OS-KSADM', - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_admin_osksadm_extension(self): r = self.admin_request(path='/v2.0/extensions/OS-KSADM') @@ -131,7 +132,7 @@ class CoreApiTests(object): 'tenantId': self.tenant_bar['id'], }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidAuthenticationResponse(r, require_service_catalog=True) def test_authenticate_unscoped(self): @@ -146,7 +147,7 @@ class CoreApiTests(object): }, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidAuthenticationResponse(r) def test_get_tenants_for_token(self): @@ -170,7 +171,7 @@ class CoreApiTests(object): 'token_id': 'invalid', }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_validate_token_service_role(self): self.md_foobar = self.assignment_api.add_role_to_user_and_project( @@ -204,7 +205,7 @@ class CoreApiTests(object): r = self.admin_request( path='/v2.0/tokens/%s' % token, token=token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_validate_token_belongs_to(self): token = self.get_scoped_token() @@ -233,7 +234,7 @@ class CoreApiTests(object): 'token_id': token, }, token=token, - expected_status=200) + expected_status=http_client.OK) def test_endpoints(self): token = self.get_scoped_token() @@ -306,7 +307,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) r = self.admin_request( @@ -321,7 +322,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) # Test UPDATE request @@ -338,7 +339,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) r = self.admin_request( @@ -351,7 +352,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) def test_create_update_user_valid_enabled_type(self): @@ -369,11 +370,12 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) def test_error_response(self): """This triggers assertValidErrorResponse by convention.""" - self.public_request(path='/v2.0/tenants', expected_status=401) + self.public_request(path='/v2.0/tenants', + expected_status=http_client.UNAUTHORIZED) def test_invalid_parameter_error_response(self): token = self.get_scoped_token() @@ -387,13 +389,13 @@ class CoreApiTests(object): path='/v2.0/OS-KSADM/services', body=bad_body, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(res) res = self.admin_request(method='POST', path='/v2.0/users', body=bad_body, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(res) def _get_user_id(self, r): @@ -457,7 +459,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) @@ -468,7 +470,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(CONF.member_role_name, self._get_role_name(r.result)) # Create a new tenant @@ -483,7 +485,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) project_id = self._get_project_id(r.result) @@ -499,7 +501,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # 'member_role' should be in new_tenant r = self.admin_request( @@ -508,7 +510,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual('_member_', self._get_role_name(r.result)) # 'member_role' should not be in tenant_bar any more @@ -518,7 +520,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertNoRoles(r.result) def test_update_user_with_invalid_tenant(self): @@ -537,7 +539,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) # Update user with an invalid tenant @@ -552,7 +554,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_user_with_invalid_tenant_no_prev_tenant(self): token = self.get_scoped_token() @@ -569,7 +571,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) # Update user with an invalid tenant @@ -584,7 +586,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_user_with_old_tenant(self): token = self.get_scoped_token() @@ -602,7 +604,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) user_id = self._get_user_id(r.result) @@ -613,7 +615,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual(CONF.member_role_name, self._get_role_name(r.result)) # Update user's tenant with old tenant id @@ -628,7 +630,7 @@ class CoreApiTests(object): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # 'member_role' should still be in tenant_bar r = self.admin_request( @@ -637,7 +639,7 @@ class CoreApiTests(object): 'user_id': user_id }, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertEqual('_member_', self._get_role_name(r.result)) def test_authenticating_a_user_with_no_password(self): @@ -669,13 +671,13 @@ class CoreApiTests(object): }, }, }, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_www_authenticate_header(self): r = self.public_request( path='/v2.0/tenants', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertEqual('Keystone uri="http://localhost"', r.headers.get('WWW-Authenticate')) @@ -684,7 +686,7 @@ class CoreApiTests(object): self.config_fixture.config(public_endpoint=test_url) r = self.public_request( path='/v2.0/tenants', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertEqual('Keystone uri="%s"' % test_url, r.headers.get('WWW-Authenticate')) @@ -719,7 +721,7 @@ class LegacyV2UsernameTests(object): path='/v2.0/users', token=token, body=body, - expected_status=200) + expected_status=http_client.OK) def test_create_with_extra_username(self): """The response for creating a user will contain the extra fields.""" @@ -770,7 +772,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -800,7 +802,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -879,7 +881,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -909,7 +911,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -929,7 +931,7 @@ class LegacyV2UsernameTests(object): 'enabled': True, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -954,7 +956,7 @@ class LegacyV2UsernameTests(object): 'enabled': enabled, }, }, - expected_status=200) + expected_status=http_client.OK) self.assertValidUserResponse(r) @@ -1141,8 +1143,9 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): return r.result['user'][attribute_name] def test_service_crud_requires_auth(self): - """Service CRUD should 401 without an X-Auth-Token (bug 1006822).""" - # values here don't matter because we should 401 before they're checked + """Service CRUD should return unauthorized without an X-Auth-Token.""" + # values here don't matter because it will be unauthorized before + # they're checked (bug 1006822). service_path = '/v2.0/OS-KSADM/services/%s' % uuid.uuid4().hex service_body = { 'OS-KSADM:service': { @@ -1153,41 +1156,43 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): r = self.admin_request(method='GET', path='/v2.0/OS-KSADM/services', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='POST', path='/v2.0/OS-KSADM/services', body=service_body, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='GET', path=service_path, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) r = self.admin_request(method='DELETE', path=service_path, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_user_role_list_requires_auth(self): - """User role list should 401 without an X-Auth-Token (bug 1006815).""" - # values here don't matter because we should 401 before they're checked + """User role list return unauthorized without an X-Auth-Token.""" + # values here don't matter because it will be unauthorized before + # they're checked (bug 1006815). path = '/v2.0/tenants/%(tenant_id)s/users/%(user_id)s/roles' % { 'tenant_id': uuid.uuid4().hex, 'user_id': uuid.uuid4().hex, } - r = self.admin_request(path=path, expected_status=401) + r = self.admin_request(path=path, + expected_status=http_client.UNAUTHORIZED) self.assertValidErrorResponse(r) def test_fetch_revocation_list_nonadmin_fails(self): self.admin_request( method='GET', path='/v2.0/tokens/revoked', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_fetch_revocation_list_admin_200(self): token = self.get_scoped_token() @@ -1195,7 +1200,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/tokens/revoked', token=token, - expected_status=200) + expected_status=http_client.OK) self.assertValidRevocationListResponse(r) def assertValidRevocationListResponse(self, response): @@ -1226,7 +1231,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/tokens/revoked', token=token1, - expected_status=200) + expected_status=http_client.OK) signed_text = r.result['signed'] data_json = cms.cms_verify(signed_text, CONF.signing.certfile, @@ -1278,7 +1283,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) # Test UPDATE request @@ -1294,7 +1299,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(r) def test_authenticating_a_user_with_an_OSKSADM_password(self): @@ -1328,7 +1333,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, }, - expected_status=200) + expected_status=http_client.OK) # ensure password doesn't leak user_id = r.result['user']['id'] @@ -1336,7 +1341,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): method='GET', path='/v2.0/users/%s' % user_id, token=token, - expected_status=200) + expected_status=http_client.OK) self.assertNotIn('OS-KSADM:password', r.result['user']) def test_updating_a_user_with_an_OSKSADM_password(self): @@ -1355,7 +1360,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, token=token, - expected_status=200) + expected_status=http_client.OK) # successfully authenticate self.public_request( @@ -1369,7 +1374,7 @@ class V2TestCase(RestfulTestCase, CoreApiTests, LegacyV2UsernameTests): }, }, }, - expected_status=200) + expected_status=http_client.OK) class RevokeApiTestCase(V2TestCase): @@ -1431,7 +1436,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=admin_token, - expected_status=200) + expected_status=http_client.OK) def test_authenticate_scoped_token(self): project_ref = self.new_project_ref() @@ -1461,7 +1466,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=admin_token, - expected_status=200) + expected_status=http_client.OK) def test_token_authentication_and_validation(self): """Test token authentication for Fernet token provider. @@ -1486,7 +1491,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): } } }, - expected_status=200) + expected_status=http_client.OK) token_id = self._get_token_id(r) path = ('/v2.0/tokens/%s?belongsTo=%s' % (token_id, project_ref['id'])) @@ -1495,7 +1500,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): method='GET', path=path, token=CONF.admin_token, - expected_status=200) + expected_status=http_client.OK) def test_rescoped_tokens_maintain_original_expiration(self): project_ref = self.new_project_ref() @@ -1517,7 +1522,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): }, # NOTE(lbragstad): This test may need to be refactored if Keystone # decides to disallow rescoping using a scoped token. - expected_status=200) + expected_status=http_client.OK) original_token = resp.result['access']['token']['id'] original_expiration = resp.result['access']['token']['expires'] @@ -1532,7 +1537,7 @@ class TestFernetTokenProviderV2(RestfulTestCase): } } }, - expected_status=200) + expected_status=http_client.OK) rescoped_token = resp.result['access']['token']['id'] rescoped_expiration = resp.result['access']['token']['expires'] self.assertNotEqual(original_token, rescoped_token) diff --git a/keystone-moon/keystone/tests/unit/test_v2_controller.py b/keystone-moon/keystone/tests/unit/test_v2_controller.py index 0d4b3cdc..581e6b9c 100644 --- a/keystone-moon/keystone/tests/unit/test_v2_controller.py +++ b/keystone-moon/keystone/tests/unit/test_v2_controller.py @@ -18,7 +18,7 @@ import uuid from keystone.assignment import controllers as assignment_controllers from keystone import exception from keystone.resource import controllers as resource_controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import database @@ -26,7 +26,7 @@ from keystone.tests.unit.ksfixtures import database _ADMIN_CONTEXT = {'is_admin': True, 'query_string': {}} -class TenantTestCase(tests.TestCase): +class TenantTestCase(unit.TestCase): """Tests for the V2 Tenant controller. These tests exercise :class:`keystone.assignment.controllers.Tenant`. diff --git a/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py b/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py index e0843605..8d6d9eb7 100644 --- a/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py +++ b/keystone-moon/keystone/tests/unit/test_v2_keystoneclient.py @@ -22,10 +22,11 @@ import mock from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import timeutils +from six.moves import http_client from six.moves import range import webob -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import default_fixtures from keystone.tests.unit.ksfixtures import appserver from keystone.tests.unit.ksfixtures import database @@ -35,11 +36,11 @@ CONF = cfg.CONF DEFAULT_DOMAIN_ID = CONF.identity.default_domain_id -class ClientDrivenTestCase(tests.TestCase): +class ClientDrivenTestCase(unit.TestCase): def config_files(self): config_files = super(ClientDrivenTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def setUp(self): @@ -1032,7 +1033,8 @@ class ClientDrivenTestCase(tests.TestCase): (new_password, self.user_two['password'])) self.public_server.application(req.environ, responseobject.start_fake_response) - self.assertEqual(403, responseobject.response_status) + self.assertEqual(http_client.FORBIDDEN, + responseobject.response_status) self.user_two['password'] = new_password self.assertRaises(client_exceptions.Unauthorized, @@ -1110,10 +1112,10 @@ class ClientDrivenTestCase(tests.TestCase): if not client: client = self.default_client url = '%s/ec2tokens' % self.default_client.auth_url - (resp, token) = client.request( + resp = client.session.request( url=url, method='POST', - body={'credentials': credentials}) - return resp, token + json={'credentials': credentials}) + return resp, resp.json() def _generate_default_user_ec2_credentials(self): cred = self. default_client.ec2.create( @@ -1135,7 +1137,7 @@ class ClientDrivenTestCase(tests.TestCase): credentials, signature = self._generate_default_user_ec2_credentials() credentials['signature'] = signature resp, token = self._send_ec2_auth_request(credentials) - self.assertEqual(200, resp.status_code) + self.assertEqual(http_client.OK, resp.status_code) self.assertIn('access', token) def test_ec2_auth_success_trust(self): @@ -1167,7 +1169,7 @@ class ClientDrivenTestCase(tests.TestCase): cred.access, cred.secret) credentials['signature'] = signature resp, token = self._send_ec2_auth_request(credentials) - self.assertEqual(200, resp.status_code) + self.assertEqual(http_client.OK, resp.status_code) self.assertEqual(trust_id, token['access']['trust']['id']) # TODO(shardy) we really want to check the roles and trustee # but because of where the stubbing happens we don't seem to diff --git a/keystone-moon/keystone/tests/unit/test_v3.py b/keystone-moon/keystone/tests/unit/test_v3.py index 9bbfa103..7afe6ad8 100644 --- a/keystone-moon/keystone/tests/unit/test_v3.py +++ b/keystone-moon/keystone/tests/unit/test_v3.py @@ -18,7 +18,7 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils from oslo_utils import timeutils -import six +from six.moves import http_client from testtools import matchers from keystone import auth @@ -27,14 +27,14 @@ from keystone.common import cache from keystone import exception from keystone import middleware from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import rest CONF = cfg.CONF DEFAULT_DOMAIN_ID = 'default' -TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%fZ' +TIME_FORMAT = unit.TIME_FORMAT class AuthTestMixin(object): @@ -116,11 +116,11 @@ class AuthTestMixin(object): return {'auth': auth_data} -class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, +class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, AuthTestMixin): def config_files(self): config_files = super(RestfulTestCase, self).config_files() - config_files.append(tests.dirs.tests_conf('backend_sql.conf')) + config_files.append(unit.dirs.tests_conf('backend_sql.conf')) return config_files def get_extensions(self): @@ -132,7 +132,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, def generate_paste_config(self): new_paste_file = None try: - new_paste_file = tests.generate_paste_config(self.EXTENSION_TO_ADD) + new_paste_file = unit.generate_paste_config(self.EXTENSION_TO_ADD) except AttributeError: # no need to report this error here, as most tests will not have # EXTENSION_TO_ADD defined. @@ -142,7 +142,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, def remove_generated_paste_config(self): try: - tests.remove_generated_paste_config(self.EXTENSION_TO_ADD) + unit.remove_generated_paste_config(self.EXTENSION_TO_ADD) except AttributeError: pass @@ -176,7 +176,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, self.load_sample_data() def _populate_default_domain(self): - if CONF.database.connection == tests.IN_MEM_DB_CONN_STRING: + if CONF.database.connection == unit.IN_MEM_DB_CONN_STRING: # NOTE(morganfainberg): If an in-memory db is being used, be sure # to populate the default domain, this is typically done by # a migration, but the in-mem db uses model definitions to create @@ -265,122 +265,52 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, self.endpoint['enabled'] = True def new_ref(self): - """Populates a ref with attributes common to all API entities.""" - return { - 'id': uuid.uuid4().hex, - 'name': uuid.uuid4().hex, - 'description': uuid.uuid4().hex, - 'enabled': True} + """Populates a ref with attributes common to some API entities.""" + return unit.new_ref() def new_region_ref(self): - ref = self.new_ref() - # Region doesn't have name or enabled. - del ref['name'] - del ref['enabled'] - ref['parent_region_id'] = None - return ref + return unit.new_region_ref() def new_service_ref(self): - ref = self.new_ref() - ref['type'] = uuid.uuid4().hex - return ref + return unit.new_service_ref() def new_endpoint_ref(self, service_id, interface='public', **kwargs): - ref = self.new_ref() - del ref['enabled'] # enabled is optional - ref['interface'] = interface - ref['service_id'] = service_id - ref['url'] = 'https://' + uuid.uuid4().hex + '.com' - ref['region_id'] = self.region_id - ref.update(kwargs) - return ref + return unit.new_endpoint_ref( + service_id, interface=interface, default_region_id=self.region_id, + **kwargs) def new_domain_ref(self): - ref = self.new_ref() - return ref + return unit.new_domain_ref() def new_project_ref(self, domain_id=None, parent_id=None, is_domain=False): - ref = self.new_ref() - ref['domain_id'] = domain_id - ref['parent_id'] = parent_id - ref['is_domain'] = is_domain - return ref + return unit.new_project_ref(domain_id=domain_id, parent_id=parent_id, + is_domain=is_domain) def new_user_ref(self, domain_id, project_id=None): - ref = self.new_ref() - ref['domain_id'] = domain_id - ref['email'] = uuid.uuid4().hex - ref['password'] = uuid.uuid4().hex - if project_id: - ref['default_project_id'] = project_id - return ref + return unit.new_user_ref(domain_id, project_id=project_id) def new_group_ref(self, domain_id): - ref = self.new_ref() - ref['domain_id'] = domain_id - return ref + return unit.new_group_ref(domain_id) def new_credential_ref(self, user_id, project_id=None, cred_type=None): - ref = dict() - ref['id'] = uuid.uuid4().hex - ref['user_id'] = user_id - if cred_type == 'ec2': - ref['type'] = 'ec2' - ref['blob'] = {'blah': 'test'} - else: - ref['type'] = 'cert' - ref['blob'] = uuid.uuid4().hex - if project_id: - ref['project_id'] = project_id - return ref + return unit.new_credential_ref(user_id, project_id=project_id, + cred_type=cred_type) def new_role_ref(self): - ref = self.new_ref() - # Roles don't have a description or the enabled flag - del ref['description'] - del ref['enabled'] - return ref + return unit.new_role_ref() def new_policy_ref(self): - ref = self.new_ref() - ref['blob'] = uuid.uuid4().hex - ref['type'] = uuid.uuid4().hex - return ref + return unit.new_policy_ref() def new_trust_ref(self, trustor_user_id, trustee_user_id, project_id=None, impersonation=None, expires=None, role_ids=None, role_names=None, remaining_uses=None, allow_redelegation=False): - ref = dict() - ref['id'] = uuid.uuid4().hex - ref['trustor_user_id'] = trustor_user_id - ref['trustee_user_id'] = trustee_user_id - ref['impersonation'] = impersonation or False - ref['project_id'] = project_id - ref['remaining_uses'] = remaining_uses - ref['allow_redelegation'] = allow_redelegation - - 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"') - - role_ids = role_ids or [] - role_names = role_names or [] - if role_ids or role_names: - ref['roles'] = [] - for role_id in role_ids: - ref['roles'].append({'id': role_id}) - for role_name in role_names: - ref['roles'].append({'name': role_name}) - - return ref + return unit.new_trust_ref( + trustor_user_id, trustee_user_id, project_id=project_id, + impersonation=impersonation, expires=expires, role_ids=role_ids, + role_names=role_names, remaining_uses=remaining_uses, + allow_redelegation=allow_redelegation) def create_new_default_project_for_user(self, user_id, domain_id, enable_project=True): @@ -482,7 +412,7 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, r = self.v3_authenticate_token(auth) return r.headers.get('X-Subject-Token') - def v3_authenticate_token(self, auth, expected_status=201): + def v3_authenticate_token(self, auth, expected_status=http_client.CREATED): return self.admin_request(method='POST', path='/v3/auth/tokens', body=auth, @@ -511,42 +441,31 @@ class RestfulTestCase(tests.SQLDriverOverrides, rest.RestfulTestCase, return self.admin_request(path=path, token=token, **kwargs) - def get(self, path, **kwargs): - r = self.v3_request(method='GET', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 200) - return r + def get(self, path, expected_status=http_client.OK, **kwargs): + return self.v3_request(path, method='GET', + expected_status=expected_status, **kwargs) - def head(self, path, **kwargs): - r = self.v3_request(method='HEAD', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) + def head(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + r = self.v3_request(path, method='HEAD', + expected_status=expected_status, **kwargs) self.assertEqual('', r.body) return r - def post(self, path, **kwargs): - r = self.v3_request(method='POST', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 201) - return r + def post(self, path, expected_status=http_client.CREATED, **kwargs): + return self.v3_request(path, method='POST', + expected_status=expected_status, **kwargs) - def put(self, path, **kwargs): - r = self.v3_request(method='PUT', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) - return r + def put(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + return self.v3_request(path, method='PUT', + expected_status=expected_status, **kwargs) - def patch(self, path, **kwargs): - r = self.v3_request(method='PATCH', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 200) - return r + def patch(self, path, expected_status=http_client.OK, **kwargs): + return self.v3_request(path, method='PATCH', + expected_status=expected_status, **kwargs) - def delete(self, path, **kwargs): - r = self.v3_request(method='DELETE', path=path, **kwargs) - if 'expected_status' not in kwargs: - self.assertResponseStatus(r, 204) - return r + def delete(self, path, expected_status=http_client.NO_CONTENT, **kwargs): + return self.v3_request(path, method='DELETE', + expected_status=expected_status, **kwargs) def assertValidErrorResponse(self, r): resp = r.result diff --git a/keystone-moon/keystone/tests/unit/test_v3_assignment.py b/keystone-moon/keystone/tests/unit/test_v3_assignment.py index 03e5d30b..f22e9f2b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_assignment.py +++ b/keystone-moon/keystone/tests/unit/test_v3_assignment.py @@ -14,11 +14,12 @@ import random import uuid from oslo_config import cfg +from six.moves import http_client from six.moves import range from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_v3 from keystone.tests.unit import utils @@ -75,9 +76,10 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'domain': ref}) self.assertValidDomainResponse(r, ref) - def test_create_domain_400(self): + def test_create_domain_bad_request(self): """Call ``POST /domains``.""" - self.post('/domains', body={'domain': {}}, expected_status=400) + self.post('/domains', body={'domain': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_domains(self): """Call ``GET /domains``.""" @@ -133,7 +135,8 @@ class AssignmentTestCase(test_v3.RestfulTestCase, } } self.admin_request( - path='/v2.0/tokens', method='POST', body=body, expected_status=401) + path='/v2.0/tokens', method='POST', body=body, + expected_status=http_client.UNAUTHORIZED) auth_data = self.build_authentication_request( user_id=self.user2['id'], @@ -160,21 +163,24 @@ class AssignmentTestCase(test_v3.RestfulTestCase, } } self.admin_request( - path='/v2.0/tokens', method='POST', body=body, expected_status=401) + path='/v2.0/tokens', method='POST', body=body, + expected_status=http_client.UNAUTHORIZED) # Try looking up in v3 by name and id auth_data = self.build_authentication_request( user_id=self.user2['id'], password=self.user2['password'], project_id=self.project2['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) auth_data = self.build_authentication_request( username=self.user2['name'], user_domain_id=self.domain2['id'], password=self.user2['password'], project_id=self.project2['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_delete_enabled_domain_fails(self): """Call ``DELETE /domains/{domain_id}`` (when domain enabled).""" @@ -357,20 +363,19 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the returned token and it should be valid. self.head('/auth/tokens', headers={'x-subject-token': subject_token}, - expected_status=200) + expected_status=http_client.OK) # now disable the domain self.domain['enabled'] = False url = "/domains/%(domain_id)s" % {'domain_id': self.domain['id']} self.patch(url, - body={'domain': {'enabled': False}}, - expected_status=200) + body={'domain': {'enabled': False}}) # validates the same token again and it should be 'not found' # as the domain has already been disabled. self.head('/auth/tokens', headers={'x-subject-token': subject_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_delete_domain_hierarchy(self): """Call ``DELETE /domains/{domain_id}``.""" @@ -485,14 +490,16 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'project': ref}) self.assertValidProjectResponse(r, ref) - def test_create_project_400(self): + def test_create_project_bad_request(self): """Call ``POST /projects``.""" - self.post('/projects', body={'project': {}}, expected_status=400) + self.post('/projects', body={'project': {}}, + expected_status=http_client.BAD_REQUEST) def test_create_project_invalid_domain_id(self): """Call ``POST /projects``.""" ref = self.new_project_ref(domain_id=uuid.uuid4().hex) - self.post('/projects', body={'project': ref}, expected_status=400) + self.post('/projects', body={'project': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_project_is_domain_not_allowed(self): """Call ``POST /projects``. @@ -504,7 +511,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, ref = self.new_project_ref(domain_id=self.domain_id, is_domain=True) self.post('/projects', body={'project': ref}, - expected_status=501) + expected_status=http_client.NOT_IMPLEMENTED) @utils.wip('waiting for projects acting as domains implementation') def test_create_project_without_parent_id_and_without_domain_id(self): @@ -644,18 +651,20 @@ class AssignmentTestCase(test_v3.RestfulTestCase, def test_get_project_with_parents_as_list_with_invalid_id(self): """Call ``GET /projects/{project_id}?parents_as_list``.""" self.get('/projects/%(project_id)s?parents_as_list' % { - 'project_id': None}, expected_status=404) + 'project_id': None}, expected_status=http_client.NOT_FOUND) self.get('/projects/%(project_id)s?parents_as_list' % { - 'project_id': uuid.uuid4().hex}, expected_status=404) + 'project_id': uuid.uuid4().hex}, + expected_status=http_client.NOT_FOUND) def test_get_project_with_subtree_as_list_with_invalid_id(self): """Call ``GET /projects/{project_id}?subtree_as_list``.""" self.get('/projects/%(project_id)s?subtree_as_list' % { - 'project_id': None}, expected_status=404) + 'project_id': None}, expected_status=http_client.NOT_FOUND) self.get('/projects/%(project_id)s?subtree_as_list' % { - 'project_id': uuid.uuid4().hex}, expected_status=404) + 'project_id': uuid.uuid4().hex}, + expected_status=http_client.NOT_FOUND) def test_get_project_with_parents_as_ids(self): """Call ``GET /projects/{project_id}?parents_as_ids``.""" @@ -766,7 +775,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.get( '/projects/%(project_id)s?parents_as_list&parents_as_ids' % { 'project_id': projects[1]['project']['id']}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_project_with_subtree_as_ids(self): """Call ``GET /projects/{project_id}?subtree_as_ids``. @@ -928,7 +937,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.get( '/projects/%(project_id)s?subtree_as_list&subtree_as_ids' % { 'project_id': projects[1]['project']['id']}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_project(self): """Call ``PATCH /projects/{project_id}``.""" @@ -965,7 +974,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, '/projects/%(project_id)s' % { 'project_id': leaf_project['id']}, body={'project': leaf_project}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_update_project_is_domain_not_allowed(self): """Call ``PATCH /projects/{project_id}`` with is_domain. @@ -981,7 +990,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.patch('/projects/%(project_id)s' % { 'project_id': resp.result['project']['id']}, body={'project': project}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_disable_leaf_project(self): """Call ``PATCH /projects/{project_id}``.""" @@ -1004,7 +1013,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, '/projects/%(project_id)s' % { 'project_id': root_project['id']}, body={'project': root_project}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_delete_project(self): """Call ``DELETE /projects/{project_id}`` @@ -1048,7 +1057,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.delete( '/projects/%(project_id)s' % { 'project_id': projects[0]['project']['id']}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Role CRUD tests @@ -1060,9 +1069,10 @@ class AssignmentTestCase(test_v3.RestfulTestCase, body={'role': ref}) return self.assertValidRoleResponse(r, ref) - def test_create_role_400(self): + def test_create_role_bad_request(self): """Call ``POST /roles``.""" - self.post('/roles', body={'role': {}}, expected_status=400) + self.post('/roles', body={'role': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_roles(self): """Call ``GET /roles``.""" @@ -1132,7 +1142,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, """Grant role on a project to a user that doesn't exist, 404 result. When grant a role on a project to a user that doesn't exist, the server - returns 404 Not Found for the user. + returns Not Found for the user. """ @@ -1145,7 +1155,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_user_domain_role_grants(self): collection_url = ( @@ -1184,7 +1194,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_group_project_role_grants(self): collection_url = ( @@ -1224,7 +1234,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def test_crud_group_domain_role_grants(self): collection_url = ( @@ -1264,7 +1274,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, 'collection_url': collection_url, 'role_id': self.role_id} - self.put(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) def _create_new_user_and_assign_role_on_project(self): """Create a new user and assign user a role on a project.""" @@ -1279,9 +1289,9 @@ class AssignmentTestCase(test_v3.RestfulTestCase, member_url = ('%(collection_url)s/%(role_id)s' % { 'collection_url': collection_url, 'role_id': self.role_id}) - self.put(member_url, expected_status=204) + self.put(member_url) # Check the user has the role assigned - self.head(member_url, expected_status=204) + self.head(member_url) return member_url, user_ref def test_delete_user_before_removing_role_assignment_succeeds(self): @@ -1290,9 +1300,9 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # Delete the user from identity backend self.identity_api.driver.delete_user(user['id']) # Clean up the role assignment - self.delete(member_url, expected_status=204) + self.delete(member_url) # Make sure the role is gone - self.head(member_url, expected_status=404) + self.head(member_url, expected_status=http_client.NOT_FOUND) def test_delete_user_and_check_role_assignment_fails(self): """Call ``DELETE`` on the user and check the role assignment.""" @@ -1301,7 +1311,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, self.identity_api.delete_user(user['id']) # We should get a 404 when looking for the user in the identity # backend because we're not performing a delete operation on the role. - self.head(member_url, expected_status=404) + self.head(member_url, expected_status=http_client.NOT_FOUND) def test_token_revoked_once_group_role_grant_revoked(self): """Test token is revoked when group role grant is revoked @@ -1333,7 +1343,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the returned token; it should be valid. self.head('/auth/tokens', headers={'x-subject-token': token}, - expected_status=200) + expected_status=http_client.OK) # revokes the grant from group on project. self.assignment_api.delete_grant(role_id=self.role['id'], @@ -1343,7 +1353,7 @@ class AssignmentTestCase(test_v3.RestfulTestCase, # validates the same token again; it should not longer be valid. self.head('/auth/tokens', headers={'x-subject-token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # Role Assignments tests @@ -1858,7 +1868,7 @@ class RoleAssignmentBaseTestCase(test_v3.RestfulTestCase, self.default_user_id = self.user_ids[0] self.default_group_id = self.group_ids[0] - def get_role_assignments(self, expected_status=200, **filters): + def get_role_assignments(self, expected_status=http_client.OK, **filters): """Returns the result from querying role assignment API + queried URL. Calls GET /v3/role_assignments?<params> and returns its result, where @@ -1903,24 +1913,24 @@ class RoleAssignmentFailureTestCase(RoleAssignmentBaseTestCase): def test_get_role_assignments_by_domain_and_project(self): self.get_role_assignments(domain_id=self.domain_id, project_id=self.project_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_user_and_group(self): self.get_role_assignments(user_id=self.default_user_id, group_id=self.default_group_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_effective_and_inherited(self): self.config_fixture.config(group='os_inherit', enabled=True) self.get_role_assignments(domain_id=self.domain_id, effective=True, inherited_to_projects=True, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_get_role_assignments_by_effective_and_group(self): self.get_role_assignments(effective=True, group_id=self.default_group_id, - expected_status=400) + expected_status=http_client.BAD_REQUEST) class RoleAssignmentDirectTestCase(RoleAssignmentBaseTestCase): @@ -2193,8 +2203,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=self.project_id) # Check the user cannot get a domain nor a project token - self.v3_authenticate_token(domain_auth_data, expected_status=401) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on domain non_inher_ud_link = self.build_role_assignment_link( @@ -2203,7 +2215,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can get only a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Create inherited role inherited_role = {'id': uuid.uuid4().hex, 'name': 'inherited'} @@ -2224,13 +2237,15 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can only get a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Delete non-inherited grant self.delete(non_inher_ud_link) # Check the user cannot get a domain token anymore - self.v3_authenticate_token(domain_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_token_from_inherited_group_domain_role_grants(self): # Create a new group and put a new user in it to @@ -2255,8 +2270,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=self.project_id) # Check the user cannot get a domain nor a project token - self.v3_authenticate_token(domain_auth_data, expected_status=401) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on domain non_inher_gd_link = self.build_role_assignment_link( @@ -2265,7 +2282,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can get only a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Create inherited role inherited_role = {'id': uuid.uuid4().hex, 'name': 'inherited'} @@ -2286,13 +2304,15 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, # Check the user can only get a domain token self.v3_authenticate_token(domain_auth_data) - self.v3_authenticate_token(project_auth_data, expected_status=401) + self.v3_authenticate_token(project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Delete non-inherited grant self.delete(non_inher_gd_link) # Check the user cannot get a domain token anymore - self.v3_authenticate_token(domain_auth_data, expected_status=401) + self.v3_authenticate_token(domain_auth_data, + expected_status=http_client.UNAUTHORIZED) def _test_crud_inherited_and_direct_assignment_on_target(self, target_url): # Create a new role to avoid assignments loaded from sample data @@ -2308,7 +2328,7 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(direct_url) # Check the direct assignment exists, but the inherited one does not self.head(direct_url) - self.head(inherited_url, expected_status=404) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) # Now add the inherited assignment self.put(inherited_url) @@ -2320,13 +2340,13 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.delete(inherited_url) # Check the direct assignment exists, but the inherited one does not self.head(direct_url) - self.head(inherited_url, expected_status=404) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) # Now delete the inherited assignment self.delete(direct_url) # Check that none of them exist - self.head(direct_url, expected_status=404) - self.head(inherited_url, expected_status=404) + self.head(direct_url, expected_status=http_client.NOT_FOUND) + self.head(inherited_url, expected_status=http_client.NOT_FOUND) def test_crud_inherited_and_direct_assignment_on_domains(self): self._test_crud_inherited_and_direct_assignment_on_target( @@ -2801,8 +2821,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=leaf_id) # Check the user cannot get a token on root nor leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for user on leaf project non_inher_up_link = self.build_role_assignment_link( @@ -2811,7 +2833,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(non_inher_up_link) # Check the user can only get a token on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Grant inherited role for user on root project @@ -2821,21 +2844,24 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(inher_up_link) # Check the user still can get a token only on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete non-inherited grant self.delete(non_inher_up_link) # Check the inherited role still applies for leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete inherited grant self.delete(inher_up_link) # Check the user cannot get a token on leaf project anymore - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_token_from_inherited_group_project_role_grants(self): # Create default scenario @@ -2858,8 +2884,10 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, project_id=leaf_id) # Check the user cannot get a token on root nor leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) # Grant non-inherited role for group on leaf project non_inher_gp_link = self.build_role_assignment_link( @@ -2868,7 +2896,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(non_inher_gp_link) # Check the user can only get a token on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Grant inherited role for group on root project @@ -2878,7 +2907,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.put(inher_gp_link) # Check the user still can get a token only on leaf project - self.v3_authenticate_token(root_project_auth_data, expected_status=401) + self.v3_authenticate_token(root_project_auth_data, + expected_status=http_client.UNAUTHORIZED) self.v3_authenticate_token(leaf_project_auth_data) # Delete no-inherited grant @@ -2891,7 +2921,8 @@ class AssignmentInheritanceTestCase(test_v3.RestfulTestCase, self.delete(inher_gp_link) # Check the user cannot get a token on leaf project anymore - self.v3_authenticate_token(leaf_project_auth_data, expected_status=401) + self.v3_authenticate_token(leaf_project_auth_data, + expected_status=http_client.UNAUTHORIZED) def test_get_role_assignments_for_project_hierarchy(self): """Call ``GET /role_assignments``. @@ -3069,13 +3100,13 @@ class AssignmentInheritanceDisabledTestCase(test_v3.RestfulTestCase): 'role_id': role['id']} collection_url = base_collection_url + '/inherited_to_projects' - self.put(member_url, expected_status=404) - self.head(member_url, expected_status=404) - self.get(collection_url, expected_status=404) - self.delete(member_url, expected_status=404) + self.put(member_url, expected_status=http_client.NOT_FOUND) + self.head(member_url, expected_status=http_client.NOT_FOUND) + self.get(collection_url, expected_status=http_client.NOT_FOUND) + self.delete(member_url, expected_status=http_client.NOT_FOUND) -class AssignmentV3toV2MethodsTestCase(tests.TestCase): +class AssignmentV3toV2MethodsTestCase(unit.TestCase): """Test domain V3 to V2 conversion methods.""" def _setup_initial_projects(self): self.project_id = uuid.uuid4().hex diff --git a/keystone-moon/keystone/tests/unit/test_v3_auth.py b/keystone-moon/keystone/tests/unit/test_v3_auth.py index 96f0ff1f..496a75c0 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_auth.py +++ b/keystone-moon/keystone/tests/unit/test_v3_auth.py @@ -22,6 +22,7 @@ from keystoneclient.common import cms import mock from oslo_config import cfg from oslo_utils import timeutils +from six.moves import http_client from six.moves import range from testtools import matchers from testtools import testcase @@ -30,7 +31,7 @@ from keystone import auth from keystone.common import utils from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.tests.unit import test_v3 @@ -141,7 +142,7 @@ class TokenAPITests(object): path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, method='GET', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_new_default_domain(self): # If the default_domain_id config option is changed, then should be @@ -199,7 +200,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_non_default_project_failed(self): # self.project is in a non-default domain @@ -213,7 +214,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_non_default_user_failed(self): self.assignment_api.create_grant( @@ -232,7 +233,7 @@ class TokenAPITests(object): method='GET', path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_domain_scope_failed(self): self.assignment_api.create_grant( @@ -250,7 +251,7 @@ class TokenAPITests(object): path='/v2.0/tokens/%s' % v3_token, token=CONF.admin_token, method='GET', - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_v3_v2_unscoped_token_intermix(self): r = self.v3_authenticate_token(self.build_authentication_request( @@ -383,14 +384,13 @@ class TokenAPITests(object): v2_token = r.result['access']['token']['id'] # Delete the v2 token using v3. - resp = self.delete( + self.delete( '/auth/tokens', headers={'X-Subject-Token': v2_token}) - self.assertEqual(resp.status_code, 204) # Attempting to use the deleted token on v2 should fail. self.admin_request( path='/v2.0/tenants', method='GET', token=v2_token, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_rescoping_token(self): expires = self.v3_token_data['token']['expires_at'] @@ -405,7 +405,8 @@ class TokenAPITests(object): self.assertEqual(expires, r.result['token']['expires_at']) def test_check_token(self): - self.head('/auth/tokens', headers=self.headers, expected_status=200) + self.head('/auth/tokens', headers=self.headers, + expected_status=http_client.OK) def test_validate_token(self): r = self.get('/auth/tokens', headers=self.headers) @@ -434,7 +435,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=self.get_scoped_token(), project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) def _v2_token(self): body = { @@ -460,7 +461,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.admin_request(path='/v2.0/tokens', method='POST', body=body, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_rescoping_v2_to_v3_disabled(self): token = self._v2_token() @@ -468,7 +469,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=token['access']['token']['id'], project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_rescoping_v3_to_v2_disabled(self): token = {'id': self.get_scoped_token()} @@ -498,7 +499,7 @@ class AllowRescopeScopedTokenDisabledTests(test_v3.RestfulTestCase): self.build_authentication_request( token=domain_scoped_token, project_id=self.project_id), - expected_status=403) + expected_status=http_client.FORBIDDEN) class TestPKITokenAPIs(test_v3.RestfulTestCase, TokenAPITests): @@ -637,7 +638,7 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): super(TestTokenRevokeSelfAndAdmin, self).config_overrides() self.config_fixture.config( group='oslo_policy', - policy_file=tests.dirs.etc('policy.v3cloudsample.json')) + policy_file=unit.dirs.etc('policy.v3cloudsample.json')) def test_user_revokes_own_token(self): user_token = self.get_requested_token( @@ -654,23 +655,29 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminA['password'], domain_name=self.domainA['name'])) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=adminA_token) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=user_token) - self.delete('/auth/tokens', headers=headers, expected_status=204, + self.delete('/auth/tokens', headers=headers, token=user_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.head('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.delete('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.delete('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.head('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) def test_adminA_revokes_userA_token(self): @@ -688,20 +695,25 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminA['password'], domain_name=self.domainA['name'])) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=adminA_token) - self.head('/auth/tokens', headers=headers, expected_status=200, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK, token=user_token) - self.delete('/auth/tokens', headers=headers, expected_status=204, + self.delete('/auth/tokens', headers=headers, token=adminA_token) - # invalid X-Auth-Token and invalid X-Subject-Token (401) - self.head('/auth/tokens', headers=headers, expected_status=401, + # invalid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.UNAUTHORIZED, token=user_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.delete('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) - # valid X-Auth-Token and invalid X-Subject-Token (404) - self.head('/auth/tokens', headers=headers, expected_status=404, + # valid X-Auth-Token and invalid X-Subject-Token + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND, token=adminA_token) def test_adminB_fails_revoking_userA_token(self): @@ -729,9 +741,11 @@ class TestTokenRevokeSelfAndAdmin(test_v3.RestfulTestCase): password=self.userAdminB['password'], domain_name=self.domainB['name'])) - self.head('/auth/tokens', headers=headers, expected_status=403, + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN, token=adminB_token) - self.delete('/auth/tokens', headers=headers, expected_status=403, + self.delete('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN, token=adminB_token) @@ -854,10 +868,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm both tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': scoped_token}, - expected_status=200) + expected_status=http_client.OK) # create a new role role = self.new_role_ref() @@ -873,10 +887,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # both tokens should remain valid self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': scoped_token}, - expected_status=200) + expected_status=http_client.OK) def test_deleting_user_grant_revokes_token(self): """Test deleting a user grant revokes token. @@ -896,7 +910,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Delete the grant, which should invalidate the token grant_url = ( '/projects/%(project_id)s/users/%(user_id)s/' @@ -907,7 +921,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.delete(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def role_data_fixtures(self): self.projectC = self.new_project_ref(domain_id=self.domainA['id']) @@ -998,19 +1012,19 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': tokenA}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenB}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenC}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenD}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': tokenE}, - expected_status=200) + expected_status=http_client.OK) # Delete the role, which should invalidate the tokens role_url = '/roles/%s' % self.role1['id'] @@ -1019,21 +1033,21 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Check the tokens that used role1 is invalid self.head('/auth/tokens', headers={'X-Subject-Token': tokenA}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenB}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenD}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': tokenE}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # ...but the one using role2 is still valid self.head('/auth/tokens', headers={'X-Subject-Token': tokenC}, - expected_status=200) + expected_status=http_client.OK) def test_domain_user_role_assignment_maintains_token(self): """Test user-domain role assignment maintains existing token. @@ -1053,7 +1067,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Assign a role, which should not affect the token grant_url = ( '/domains/%(domain_id)s/users/%(user_id)s/' @@ -1064,7 +1078,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.put(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) def test_disabling_project_revokes_token(self): token = self.get_requested_token( @@ -1076,7 +1090,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # disable the project, which should invalidate the token self.patch( @@ -1086,13 +1100,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # user should no longer have access to the project self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], password=self.user3['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_deleting_project_revokes_token(self): token = self.get_requested_token( @@ -1104,7 +1118,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # delete the project, which should invalidate the token self.delete( @@ -1113,13 +1127,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # user should no longer have access to the project self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], password=self.user3['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_deleting_group_grant_revokes_tokens(self): """Test deleting a group grant revokes tokens. @@ -1153,13 +1167,13 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token3}, - expected_status=200) + expected_status=http_client.OK) # Delete the group grant, which should invalidate the # tokens for user1 and user2 grant_url = ( @@ -1171,15 +1185,15 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.delete(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # But user3's token should be invalid too as revocation is done for # scope role & project self.head('/auth/tokens', headers={'X-Subject-Token': token3}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_domain_group_role_assignment_maintains_token(self): """Test domain-group role assignment maintains existing token. @@ -1199,7 +1213,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm token is valid self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Delete the grant, which should invalidate the token grant_url = ( '/domains/%(domain_id)s/groups/%(group_id)s/' @@ -1210,7 +1224,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): self.put(grant_url) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) def test_group_membership_changes_revokes_token(self): """Test add/removal to/from group revokes token. @@ -1240,10 +1254,10 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # Confirm tokens are valid self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=200) + expected_status=http_client.OK) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) # Remove user1 from group1, which should invalidate # the token self.delete('/groups/%(group_id)s/users/%(user_id)s' % { @@ -1251,18 +1265,18 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): 'user_id': self.user1['id']}) self.head('/auth/tokens', headers={'X-Subject-Token': token1}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # But user2's token should still be valid self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) # Adding user2 to a group should not invalidate token self.put('/groups/%(group_id)s/users/%(user_id)s' % { 'group_id': self.group2['id'], 'user_id': self.user2['id']}) self.head('/auth/tokens', headers={'X-Subject-Token': token2}, - expected_status=200) + expected_status=http_client.OK) def test_removing_role_assignment_does_not_affect_other_users(self): """Revoking a role from one user should not affect other users.""" @@ -1295,18 +1309,18 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # authorization for the first user should now fail self.head('/auth/tokens', headers={'X-Subject-Token': user1_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user1['id'], password=self.user1['password'], project_id=self.projectA['id']), - expected_status=401) + expected_status=http_client.UNAUTHORIZED) # authorization for the second user should still succeed self.head('/auth/tokens', headers={'X-Subject-Token': user3_token}, - expected_status=200) + expected_status=http_client.OK) self.v3_authenticate_token( self.build_authentication_request( user_id=self.user3['id'], @@ -1329,7 +1343,7 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): '/projects/%(project_id)s' % {'project_id': self.projectA['id']}) # Make sure that we get a NotFound(404) when heading that role. - self.head(role_path, expected_status=404) + self.head(role_path, expected_status=http_client.NOT_FOUND) def get_v2_token(self, token=None, project_id=None): body = {'auth': {}, } @@ -1356,12 +1370,11 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): token = self.get_v2_token() self.delete('/auth/tokens', - headers={'X-Subject-Token': token}, - expected_status=204) + headers={'X-Subject-Token': token}) self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_revoke_token_from_token(self): # Test that a scoped token can be requested from an unscoped token, @@ -1387,38 +1400,36 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # revoke the project-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': project_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': project_scoped_token}) # The project-scoped token is invalidated. self.head('/auth/tokens', headers={'X-Subject-Token': project_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) # The domain-scoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': domain_scoped_token}, - expected_status=200) + expected_status=http_client.OK) # revoke the domain-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': domain_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': domain_scoped_token}) # The domain-scoped token is invalid. self.head('/auth/tokens', headers={'X-Subject-Token': domain_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) def test_revoke_token_from_token_v2(self): # Test that a scoped token can be requested from an unscoped token, @@ -1436,18 +1447,17 @@ class TestTokenRevokeById(test_v3.RestfulTestCase): # revoke the project-scoped token. self.delete('/auth/tokens', - headers={'X-Subject-Token': project_scoped_token}, - expected_status=204) + headers={'X-Subject-Token': project_scoped_token}) # The project-scoped token is invalidated. self.head('/auth/tokens', headers={'X-Subject-Token': project_scoped_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # The unscoped token should still be valid. self.head('/auth/tokens', headers={'X-Subject-Token': unscoped_token}, - expected_status=200) + expected_status=http_client.OK) class TestTokenRevokeByAssignment(TestTokenRevokeById): @@ -1491,11 +1501,11 @@ class TestTokenRevokeByAssignment(TestTokenRevokeById): # authorization for the projectA should still succeed self.head('/auth/tokens', headers={'X-Subject-Token': other_project_token}, - expected_status=200) + expected_status=http_client.OK) # while token for the projectB should not self.head('/auth/tokens', headers={'X-Subject-Token': project_token}, - expected_status=404) + expected_status=http_client.NOT_FOUND) revoked_tokens = [ t['id'] for t in self.token_provider_api.list_revoked_tokens()] # token is in token revocation list @@ -1553,57 +1563,53 @@ class TestTokenRevokeApi(TestTokenRevokeById): def test_revoke_token(self): scoped_token = self.get_scoped_token() headers = {'X-Subject-Token': scoped_token} - response = self.get('/auth/tokens', headers=headers, - expected_status=200).json_body['token'] + response = self.get('/auth/tokens', headers=headers).json_body['token'] - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.head('/auth/tokens', headers=headers, expected_status=404) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + self.delete('/auth/tokens', headers=headers) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidRevokedTokenResponse(events_response, audit_id=response['audit_ids'][0]) def test_revoke_v2_token(self): token = self.get_v2_token() headers = {'X-Subject-Token': token} - response = self.get('/auth/tokens', headers=headers, - expected_status=200).json_body['token'] - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.head('/auth/tokens', headers=headers, expected_status=404) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + response = self.get('/auth/tokens', + headers=headers).json_body['token'] + self.delete('/auth/tokens', headers=headers) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidRevokedTokenResponse( events_response, audit_id=response['audit_ids'][0]) def test_revoke_by_id_false_410(self): - self.get('/auth/tokens/OS-PKI/revoked', expected_status=410) + self.get('/auth/tokens/OS-PKI/revoked', + expected_status=http_client.GONE) def test_list_delete_project_shows_in_event_list(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) self.delete( '/projects/%(project_id)s' % {'project_id': self.projectA['id']}) - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events_response = self.get('/OS-REVOKE/events').json_body self.assertValidDeletedProjectResponse(events_response, self.projectA['id']) def test_disable_domain_shows_in_event_list(self): - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) disable_body = {'domain': {'enabled': False}} self.patch( '/domains/%(project_id)s' % {'project_id': self.domainA['id']}, body=disable_body) - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events = self.get('/OS-REVOKE/events').json_body self.assertDomainInList(events, self.domainA['id']) @@ -1633,8 +1639,7 @@ class TestTokenRevokeApi(TestTokenRevokeById): def test_list_delete_token_shows_in_event_list(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual([], events) scoped_token = self.get_scoped_token() @@ -1648,47 +1653,50 @@ class TestTokenRevokeApi(TestTokenRevokeById): response.json_body['token'] headers3 = {'X-Subject-Token': response.headers['X-Subject-Token']} - self.head('/auth/tokens', headers=headers, expected_status=200) - self.head('/auth/tokens', headers=headers2, expected_status=200) - self.head('/auth/tokens', headers=headers3, expected_status=200) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers2, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers3, + expected_status=http_client.OK) - self.delete('/auth/tokens', headers=headers, expected_status=204) + self.delete('/auth/tokens', headers=headers) # NOTE(ayoung): not deleting token3, as it should be deleted # by previous - events_response = self.get('/OS-REVOKE/events', - expected_status=200).json_body + events_response = self.get('/OS-REVOKE/events').json_body events = events_response['events'] self.assertEqual(1, len(events)) self.assertEventDataInList( events, audit_id=token2['audit_ids'][1]) - self.head('/auth/tokens', headers=headers, expected_status=404) - self.head('/auth/tokens', headers=headers2, expected_status=200) - self.head('/auth/tokens', headers=headers3, expected_status=200) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) + self.head('/auth/tokens', headers=headers2, + expected_status=http_client.OK) + self.head('/auth/tokens', headers=headers3, + expected_status=http_client.OK) def test_list_with_filter(self): self.role_data_fixtures() - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual(0, len(events)) scoped_token = self.get_scoped_token() headers = {'X-Subject-Token': scoped_token} auth = self.build_authentication_request(token=scoped_token) headers2 = {'X-Subject-Token': self.get_requested_token(auth)} - self.delete('/auth/tokens', headers=headers, expected_status=204) - self.delete('/auth/tokens', headers=headers2, expected_status=204) + self.delete('/auth/tokens', headers=headers) + self.delete('/auth/tokens', headers=headers2) - events = self.get('/OS-REVOKE/events', - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events').json_body['events'] self.assertEqual(2, len(events)) future = utils.isotime(timeutils.utcnow() + datetime.timedelta(seconds=1000)) - events = self.get('/OS-REVOKE/events?since=%s' % (future), - expected_status=200).json_body['events'] + events = self.get('/OS-REVOKE/events?since=%s' % (future) + ).json_body['events'] self.assertEqual(0, len(events)) @@ -2002,7 +2010,7 @@ class TestAuth(test_v3.RestfulTestCase): self._check_disabled_endpoint_result(r.result['token']['catalog'], disabled_endpoint_id) - def test_project_id_scoped_token_with_user_id_401(self): + def test_project_id_scoped_token_with_user_id_unauthorized(self): project = self.new_project_ref(domain_id=self.domain_id) self.resource_api.create_project(project['id'], project) @@ -2010,7 +2018,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=project['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_user_and_group_roles_scoped_token(self): """Test correct roles are returned in scoped token. @@ -2346,7 +2355,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], domain_id=self.domain['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_auth_with_id(self): auth_data = self.build_authentication_request( @@ -2395,34 +2405,39 @@ class TestAuth(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( user_id=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_user_name(self): auth_data = self.build_authentication_request( username=uuid.uuid4().hex, user_domain_id=self.domain['id'], password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_domain_id(self): auth_data = self.build_authentication_request( username=self.user['name'], user_domain_id=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_domain_name(self): auth_data = self.build_authentication_request( username=self.user['name'], user_domain_name=uuid.uuid4().hex, password=self.user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_invalid_password(self): auth_data = self.build_authentication_request( user_id=self.user['id'], password=uuid.uuid4().hex) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_remote_user_no_realm(self): api = auth.controllers.Auth() @@ -2588,7 +2603,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=user['id'], password='password') - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_disabled_default_project_result_in_unscoped_token(self): # create a disabled project to work with @@ -2666,7 +2682,8 @@ class TestAuth(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=project['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) # user should not be able to auth with project_name & domain auth_data = self.build_authentication_request( @@ -2674,7 +2691,8 @@ class TestAuth(test_v3.RestfulTestCase): password=self.user['password'], project_name=project['name'], project_domain_id=domain['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_auth_methods_with_different_identities_fails(self): # get the token for a user. This is self.user which is different from @@ -2686,7 +2704,8 @@ class TestAuth(test_v3.RestfulTestCase): token=token, user_id=self.default_domain_user['id'], password=self.default_domain_user['password']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) class TestAuthJSONExternal(test_v3.RestfulTestCase): @@ -2712,15 +2731,18 @@ class TestTrustOptional(test_v3.RestfulTestCase): self.config_fixture.config(group='trust', enabled=False) def test_trusts_404(self): - self.get('/OS-TRUST/trusts', body={'trust': {}}, expected_status=404) - self.post('/OS-TRUST/trusts', body={'trust': {}}, expected_status=404) + self.get('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.NOT_FOUND) + self.post('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.NOT_FOUND) - def test_auth_with_scope_in_trust_403(self): + def test_auth_with_scope_in_trust_forbidden(self): auth_data = self.build_authentication_request( user_id=self.user['id'], password=self.user['password'], trust_id=uuid.uuid4().hex) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) class TestTrustRedelegation(test_v3.RestfulTestCase): @@ -2804,7 +2826,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_modified_redelegation_count_error(self): r = self.post('/OS-TRUST/trusts', @@ -2820,14 +2842,14 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_max_redelegation_count_constraint(self): incorrect = CONF.trust.max_redelegation_count + 1 self.redelegated_trust_ref['redelegation_count'] = incorrect self.post('/OS-TRUST/trusts', body={'trust': self.redelegated_trust_ref}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_expiry(self): r = self.post('/OS-TRUST/trusts', @@ -2847,7 +2869,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': too_long_live_chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_remaining_uses(self): r = self.post('/OS-TRUST/trusts', @@ -2862,7 +2884,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_roles_subset(self): # Build second role @@ -2949,7 +2971,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': self.chained_trust_ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_redelegation_terminator(self): r = self.post('/OS-TRUST/trusts', @@ -2977,7 +2999,7 @@ class TestTrustRedelegation(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) class TestTrustChain(test_v3.RestfulTestCase): @@ -3084,22 +3106,20 @@ class TestTrustChain(test_v3.RestfulTestCase): def test_delete_trust_cascade(self): self.assert_user_authenticate(self.user_chain[0]) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[0]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[0]['id']}) headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=404) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) self.assert_trust_tokens_revoked(self.trust_chain[0]['id']) def test_delete_broken_chain(self): self.assert_user_authenticate(self.user_chain[0]) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[1]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[1]['id']}) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': self.trust_chain[0]['id']}, - expected_status=204) + 'trust_id': self.trust_chain[0]['id']}) def test_trustor_roles_revoked(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3111,7 +3131,8 @@ class TestTrustChain(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( token=self.last_token, trust_id=self.trust_chain[-1]['id']) - self.v3_authenticate_token(auth_data, expected_status=404) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_FOUND) def test_intermediate_user_disabled(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3123,7 +3144,8 @@ class TestTrustChain(test_v3.RestfulTestCase): # Bypass policy enforcement with mock.patch.object(rules, 'enforce', return_value=True): headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=403) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN) def test_intermediate_user_deleted(self): self.assert_user_authenticate(self.user_chain[0]) @@ -3133,7 +3155,8 @@ class TestTrustChain(test_v3.RestfulTestCase): # Bypass policy enforcement with mock.patch.object(rules, 'enforce', return_value=True): headers = {'X-Subject-Token': self.last_token} - self.head('/auth/tokens', headers=headers, expected_status=403) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.FORBIDDEN) class TestTrustAuth(test_v3.RestfulTestCase): @@ -3159,9 +3182,10 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.trustee_user['password'] = password self.trustee_user_id = self.trustee_user['id'] - def test_create_trust_400(self): + def test_create_trust_bad_request(self): # The server returns a 403 Forbidden rather than a 400, see bug 1133435 - self.post('/OS-TRUST/trusts', body={'trust': {}}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': {}}, + expected_status=http_client.FORBIDDEN) def test_create_unscoped_trust(self): ref = self.new_trust_ref( @@ -3175,7 +3199,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustor_user_id=self.user_id, trustee_user_id=self.trustee_user_id, project_id=self.project_id) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.FORBIDDEN) def _initialize_test_consume_trust(self, count): # Make sure remaining_uses is decremented as we consume the trust @@ -3189,8 +3214,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): # make sure the trust exists trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) # get a token for the trustee auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], @@ -3208,8 +3232,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self._initialize_test_consume_trust(2) # check decremented value r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) trust = r.result.get('trust') self.assertIsNotNone(trust) self.assertEqual(1, trust['remaining_uses']) @@ -3219,13 +3242,14 @@ class TestTrustAuth(test_v3.RestfulTestCase): # No more uses, the trust is made unavailable self.get( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) # this time we can't get a trust token auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_create_trust_with_bad_values_for_remaining_uses(self): # negative values for the remaining_uses parameter are forbidden @@ -3245,7 +3269,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): role_ids=[self.role_id]) self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_invalid_trust_request_without_impersonation(self): ref = self.new_trust_ref( @@ -3258,7 +3282,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_invalid_trust_request_without_trustee(self): ref = self.new_trust_ref( @@ -3271,7 +3295,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_unlimited_use_trust(self): # by default trusts are unlimited in terms of tokens that can be @@ -3286,8 +3310,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password']) @@ -3298,8 +3321,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust_id=trust['id']) r = self.v3_authenticate_token(auth_data) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) trust = r.result.get('trust') self.assertIsNone(trust['remaining_uses']) @@ -3313,45 +3335,41 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) r = self.get( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=200) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) self.assertValidTrustResponse(r, ref) # validate roles on the trust r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles' % { - 'trust_id': trust['id']}, - expected_status=200) + 'trust_id': trust['id']}) roles = self.assertValidRoleListResponse(r, self.role) self.assertIn(self.role['id'], [x['id'] for x in roles]) self.head( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], 'role_id': self.role['id']}, - expected_status=200) + expected_status=http_client.OK) r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], - 'role_id': self.role['id']}, - expected_status=200) + 'role_id': self.role['id']}) self.assertValidRoleResponse(r, self.role) - r = self.get('/OS-TRUST/trusts', expected_status=200) + r = self.get('/OS-TRUST/trusts') self.assertValidTrustListResponse(r, trust) # trusts are immutable self.patch( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, body={'trust': ref}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.delete( - '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=204) + '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}) self.get( '/OS-TRUST/trusts/%(trust_id)s' % {'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_create_trust_trustee_404(self): ref = self.new_trust_ref( @@ -3359,7 +3377,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=uuid.uuid4().hex, project_id=self.project_id, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_trustor_trustee_backwards(self): ref = self.new_trust_ref( @@ -3367,7 +3386,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.user_id, project_id=self.project_id, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=403) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.FORBIDDEN) def test_create_trust_project_404(self): ref = self.new_trust_ref( @@ -3375,7 +3395,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=uuid.uuid4().hex, role_ids=[self.role_id]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_role_id_404(self): ref = self.new_trust_ref( @@ -3383,7 +3404,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=self.project_id, role_ids=[uuid.uuid4().hex]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_create_trust_role_name_404(self): ref = self.new_trust_ref( @@ -3391,7 +3413,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): trustee_user_id=self.trustee_user_id, project_id=self.project_id, role_names=[uuid.uuid4().hex]) - self.post('/OS-TRUST/trusts', body={'trust': ref}, expected_status=404) + self.post('/OS-TRUST/trusts', body={'trust': ref}, + expected_status=http_client.NOT_FOUND) def test_v3_v2_intermix_trustor_not_in_default_domain_failed(self): ref = self.new_trust_ref( @@ -3419,7 +3442,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_trustor_not_in_default_domaini_failed(self): ref = self.new_trust_ref( @@ -3452,7 +3475,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix_project_not_in_default_domaini_failed(self): # create a trustee in default domain to delegate stuff to @@ -3492,7 +3515,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=401) + method='GET', expected_status=http_client.UNAUTHORIZED) def test_v3_v2_intermix(self): # create a trustee in default domain to delegate stuff to @@ -3531,7 +3554,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): path = '/v2.0/tokens/%s' % (token) self.admin_request( path=path, token=CONF.admin_token, - method='GET', expected_status=200) + method='GET', expected_status=http_client.OK) def test_exercise_trust_scoped_token_without_impersonation(self): ref = self.new_trust_ref( @@ -3624,7 +3647,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=trust_token, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_trust_deleted_grant(self): # create a new role @@ -3662,7 +3685,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - r = self.v3_authenticate_token(auth_data, expected_status=403) + r = self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def test_trust_chained(self): """Test that a trust token can't be used to execute another trust. @@ -3730,11 +3754,11 @@ class TestTrustAuth(test_v3.RestfulTestCase): auth_data = self.build_authentication_request( token=trust_token, trust_id=trust1['id']) - r = self.v3_authenticate_token(auth_data, expected_status=403) + r = self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def assertTrustTokensRevoked(self, trust_id): - revocation_response = self.get('/OS-REVOKE/events', - expected_status=200) + revocation_response = self.get('/OS-REVOKE/events') revocation_events = revocation_response.json_body['events'] found = False for event in revocation_events: @@ -3763,10 +3787,10 @@ class TestTrustAuth(test_v3.RestfulTestCase): r, self.trustee_user) trust_token = r.headers['X-Subject-Token'] self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': trust_id}, - expected_status=204) + 'trust_id': trust_id}) headers = {'X-Subject-Token': trust_token} - self.head('/auth/tokens', headers=headers, expected_status=404) + self.head('/auth/tokens', headers=headers, + expected_status=http_client.NOT_FOUND) self.assertTrustTokensRevoked(trust_id) def disable_user(self, user): @@ -3790,7 +3814,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=201) + self.v3_authenticate_token(auth_data) self.disable_user(self.user) @@ -3798,7 +3822,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) def test_trust_get_token_fails_if_trustee_disabled(self): ref = self.new_trust_ref( @@ -3817,7 +3842,7 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=201) + self.v3_authenticate_token(auth_data) self.disable_user(self.trustee_user) @@ -3825,7 +3850,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_delete_trust(self): ref = self.new_trust_ref( @@ -3841,22 +3867,22 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust = self.assertValidTrustResponse(r, ref) self.delete('/OS-TRUST/trusts/%(trust_id)s' % { - 'trust_id': trust['id']}, - expected_status=204) + 'trust_id': trust['id']}) self.get('/OS-TRUST/trusts/%(trust_id)s' % { 'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) self.get('/OS-TRUST/trusts/%(trust_id)s' % { 'trust_id': trust['id']}, - expected_status=404) + expected_status=http_client.NOT_FOUND) auth_data = self.build_authentication_request( user_id=self.trustee_user['id'], password=self.trustee_user['password'], trust_id=trust['id']) - self.v3_authenticate_token(auth_data, expected_status=401) + self.v3_authenticate_token(auth_data, + expected_status=http_client.UNAUTHORIZED) def test_list_trusts(self): ref = self.new_trust_ref( @@ -3871,19 +3897,19 @@ class TestTrustAuth(test_v3.RestfulTestCase): r = self.post('/OS-TRUST/trusts', body={'trust': ref}) self.assertValidTrustResponse(r, ref) - r = self.get('/OS-TRUST/trusts', expected_status=200) + r = self.get('/OS-TRUST/trusts') trusts = r.result['trusts'] self.assertEqual(3, len(trusts)) self.assertValidTrustListResponse(r) r = self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=200) + self.user_id) trusts = r.result['trusts'] self.assertEqual(3, len(trusts)) self.assertValidTrustListResponse(r) r = self.get('/OS-TRUST/trusts?trustee_user_id=%s' % - self.user_id, expected_status=200) + self.user_id) trusts = r.result['trusts'] self.assertEqual(0, len(trusts)) @@ -3909,16 +3935,14 @@ class TestTrustAuth(test_v3.RestfulTestCase): trust_token = r.headers.get('X-Subject-Token') self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=200, - token=trust_token) + self.user_id, token=trust_token) self.assertValidUserResponse( self.patch('/users/%s' % self.trustee_user['id'], - body={'user': {'password': uuid.uuid4().hex}}, - expected_status=200)) + body={'user': {'password': uuid.uuid4().hex}})) self.get('/OS-TRUST/trusts?trustor_user_id=%s' % - self.user_id, expected_status=401, + self.user_id, expected_status=http_client.UNAUTHORIZED, token=trust_token) def test_trustee_can_do_role_ops(self): @@ -3947,14 +3971,13 @@ class TestTrustAuth(test_v3.RestfulTestCase): 'trust_id': trust['id'], 'role_id': self.role['id']}, auth=auth_data, - expected_status=200) + expected_status=http_client.OK) r = self.get( '/OS-TRUST/trusts/%(trust_id)s/roles/%(role_id)s' % { 'trust_id': trust['id'], 'role_id': self.role['id']}, - auth=auth_data, - expected_status=200) + auth=auth_data) self.assertValidRoleResponse(r, self.role) def test_do_not_consume_remaining_uses_when_get_token_fails(self): @@ -3977,7 +4000,8 @@ class TestTrustAuth(test_v3.RestfulTestCase): user_id=self.default_domain_user['id'], password=self.default_domain_user['password'], trust_id=trust_id) - self.v3_authenticate_token(auth_data, expected_status=403) + self.v3_authenticate_token(auth_data, + expected_status=http_client.FORBIDDEN) r = self.get('/OS-TRUST/trusts/%s' % trust_id) self.assertEqual(3, r.result.get('trust').get('remaining_uses')) @@ -3998,10 +4022,10 @@ class TestAPIProtectionWithoutAuthContextMiddleware(test_v3.RestfulTestCase): 'query_string': {}, 'environment': {}} r = auth_controller.validate_token(context) - self.assertEqual(200, r.status_code) + self.assertEqual(http_client.OK, r.status_code) -class TestAuthContext(tests.TestCase): +class TestAuthContext(unit.TestCase): def setUp(self): super(TestAuthContext, self).setUp() self.auth_context = auth.controllers.AuthContext() @@ -4058,9 +4082,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): def test_get_catalog_project_scoped_token(self): """Call ``GET /auth/catalog`` with a project-scoped token.""" - r = self.get( - '/auth/catalog', - expected_status=200) + r = self.get('/auth/catalog') self.assertValidCatalogResponse(r) def test_get_catalog_domain_scoped_token(self): @@ -4075,7 +4097,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], domain_id=self.domain['id']), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_get_catalog_unscoped_token(self): """Call ``GET /auth/catalog`` with an unscoped token.""" @@ -4084,17 +4106,17 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): auth=self.build_authentication_request( user_id=self.default_domain_user['id'], password=self.default_domain_user['password']), - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_get_catalog_no_token(self): """Call ``GET /auth/catalog`` without a token.""" self.get( '/auth/catalog', noauth=True, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_get_projects_project_scoped_token(self): - r = self.get('/auth/projects', expected_status=200) + r = self.get('/auth/projects') self.assertThat(r.json['projects'], matchers.HasLength(1)) self.assertValidProjectListResponse(r) @@ -4102,7 +4124,7 @@ class TestAuthSpecificData(test_v3.RestfulTestCase): self.put(path='/domains/%s/users/%s/roles/%s' % ( self.domain['id'], self.user['id'], self.role['id'])) - r = self.get('/auth/domains', expected_status=200) + r = self.get('/auth/domains') self.assertThat(r.json['domains'], matchers.HasLength(1)) self.assertValidDomainListResponse(r) @@ -4113,7 +4135,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) def _make_auth_request(self, auth_data): - resp = self.post('/auth/tokens', body=auth_data, expected_status=201) + resp = self.post('/auth/tokens', body=auth_data) token = resp.headers.get('X-Subject-Token') self.assertLess(len(token), 255) return token @@ -4145,13 +4167,13 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): trust_id=trust['id']) return self._make_auth_request(auth_data) - def _validate_token(self, token, expected_status=200): + def _validate_token(self, token, expected_status=http_client.OK): return self.get( '/auth/tokens', headers={'X-Subject-Token': token}, expected_status=expected_status) - def _revoke_token(self, token, expected_status=204): + def _revoke_token(self, token, expected_status=http_client.NO_CONTENT): return self.delete( '/auth/tokens', headers={'X-Subject-Token': token}, @@ -4190,13 +4212,15 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): unscoped_token = self._get_unscoped_token() tampered_token = (unscoped_token[:50] + uuid.uuid4().hex + unscoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_unscoped_token(self): unscoped_token = self._get_unscoped_token() self._validate_token(unscoped_token) self._revoke_token(unscoped_token) - self._validate_token(unscoped_token, expected_status=404) + self._validate_token(unscoped_token, + expected_status=http_client.NOT_FOUND) def test_unscoped_token_is_invalid_after_disabling_user(self): unscoped_token = self._get_unscoped_token() @@ -4270,13 +4294,15 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): project_scoped_token = self._get_project_scoped_token() tampered_token = (project_scoped_token[:50] + uuid.uuid4().hex + project_scoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_project_scoped_token(self): project_scoped_token = self._get_project_scoped_token() self._validate_token(project_scoped_token) self._revoke_token(project_scoped_token) - self._validate_token(project_scoped_token, expected_status=404) + self._validate_token(project_scoped_token, + expected_status=http_client.NOT_FOUND) def test_project_scoped_token_is_invalid_after_disabling_user(self): project_scoped_token = self._get_project_scoped_token() @@ -4378,7 +4404,8 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): # Get a trust scoped token tampered_token = (trust_scoped_token[:50] + uuid.uuid4().hex + trust_scoped_token[50 + 32:]) - self._validate_token(tampered_token, expected_status=404) + self._validate_token(tampered_token, + expected_status=http_client.NOT_FOUND) def test_revoke_trust_scoped_token(self): trustee_user, trust = self._create_trust() @@ -4386,7 +4413,8 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): # Validate a trust scoped token self._validate_token(trust_scoped_token) self._revoke_token(trust_scoped_token) - self._validate_token(trust_scoped_token, expected_status=404) + self._validate_token(trust_scoped_token, + expected_status=http_client.NOT_FOUND) def test_trust_scoped_token_is_invalid_after_disabling_trustee(self): trustee_user, trust = self._create_trust() @@ -4460,7 +4488,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.token_provider_api.validate_token, trust_scoped_token) - def test_v2_validate_unscoped_token_returns_401(self): + def test_v2_validate_unscoped_token_returns_unauthorized(self): """Test raised exception when validating unscoped token. Test that validating an unscoped token in v2.0 of a v3 user of a @@ -4471,7 +4499,7 @@ class TestFernetTokenProvider(test_v3.RestfulTestCase): self.token_provider_api.validate_v2_token, unscoped_token) - def test_v2_validate_domain_scoped_token_returns_401(self): + def test_v2_validate_domain_scoped_token_returns_unauthorized(self): """Test raised exception when validating a domain scoped token. Test that validating an domain scoped token in v2.0 @@ -4519,7 +4547,8 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_app.extra_environ.update({'REMOTE_USER': remote_user, 'AUTH_TYPE': 'Negotiate'}) # Bind not current supported by Fernet, see bug 1433311. - self.v3_authenticate_token(auth_data, expected_status=501) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_IMPLEMENTED) def test_v2_v3_bind_token_intermix(self): self.config_fixture.config(group='token', bind='kerberos') @@ -4534,7 +4563,7 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_request(path='/v2.0/tokens', method='POST', body=body, - expected_status=501) + expected_status=http_client.NOT_IMPLEMENTED) def test_auth_with_bind_token(self): self.config_fixture.config(group='token', bind=['kerberos']) @@ -4544,4 +4573,5 @@ class TestAuthFernetTokenProvider(TestAuth): self.admin_app.extra_environ.update({'REMOTE_USER': remote_user, 'AUTH_TYPE': 'Negotiate'}) # Bind not current supported by Fernet, see bug 1433311. - self.v3_authenticate_token(auth_data, expected_status=501) + self.v3_authenticate_token(auth_data, + expected_status=http_client.NOT_IMPLEMENTED) diff --git a/keystone-moon/keystone/tests/unit/test_v3_catalog.py b/keystone-moon/keystone/tests/unit/test_v3_catalog.py index f96b2a12..0d82390d 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_catalog.py +++ b/keystone-moon/keystone/tests/unit/test_v3_catalog.py @@ -15,10 +15,11 @@ import copy import uuid +from six.moves import http_client from testtools import matchers from keystone import catalog -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import database from keystone.tests.unit import test_v3 @@ -35,7 +36,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): r = self.put( '/regions/%s' % region_id, body={'region': ref}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidRegionResponse(r, ref) # Double-check that the region ID was kept as-is and not # populated with a UUID, as is the case with POST /v3/regions @@ -48,7 +49,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): r = self.put( '/regions/%s' % region_id, body={'region': ref}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidRegionResponse(r, ref) # Double-check that the region ID was kept as-is and not # populated with a UUID, as is the case with POST /v3/regions @@ -59,7 +60,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = dict(description="my region") self.put( '/regions/myregion', - body={'region': ref}, expected_status=201) + body={'region': ref}, expected_status=http_client.CREATED) # Create region again with duplicate id self.put( '/regions/myregion', @@ -85,9 +86,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_region_ref() ref['id'] = '' - r = self.post( - '/regions', - body={'region': ref}, expected_status=201) + r = self.post('/regions', body={'region': ref}) self.assertValidRegionResponse(r, ref) self.assertNotEmpty(r.result['region'].get('id')) @@ -99,10 +98,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref['id'] # let the service define the ID - r = self.post( - '/regions', - body={'region': ref}, - expected_status=201) + r = self.post('/regions', body={'region': ref}) self.assertValidRegionResponse(r, ref) def test_create_region_without_description(self): @@ -111,10 +107,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref['description'] - r = self.post( - '/regions', - body={'region': ref}, - expected_status=201) + r = self.post('/regions', body={'region': ref}) # Create the description in the reference to compare to since the # response should now have a description, even though we didn't send # it with the original reference. @@ -134,16 +127,10 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref1['description'] = region_desc ref2['description'] = region_desc - resp1 = self.post( - '/regions', - body={'region': ref1}, - expected_status=201) + resp1 = self.post('/regions', body={'region': ref1}) self.assertValidRegionResponse(resp1, ref1) - resp2 = self.post( - '/regions', - body={'region': ref2}, - expected_status=201) + resp2 = self.post('/regions', body={'region': ref2}) self.assertValidRegionResponse(resp2, ref2) def test_create_regions_without_descriptions(self): @@ -158,15 +145,9 @@ class CatalogTestCase(test_v3.RestfulTestCase): del ref1['description'] ref2['description'] = None - resp1 = self.post( - '/regions', - body={'region': ref1}, - expected_status=201) + resp1 = self.post('/regions', body={'region': ref1}) - resp2 = self.post( - '/regions', - body={'region': ref2}, - expected_status=201) + resp2 = self.post('/regions', body={'region': ref2}) # Create the descriptions in the references to compare to since the # responses should now have descriptions, even though we didn't send # a description with the original references. @@ -184,7 +165,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.put( '/regions/%s' % uuid.uuid4().hex, body={'region': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_list_regions(self): """Call ``GET /regions``.""" @@ -230,16 +211,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): """Call ``PATCH /regions/{region_id}``.""" region_ref = self.new_region_ref() - resp = self.post('/regions', body={'region': region_ref}, - expected_status=201) + resp = self.post('/regions', body={'region': region_ref}) region_updates = { # update with something that's not the description 'parent_region_id': self.region_id, } resp = self.patch('/regions/%s' % region_ref['id'], - body={'region': region_updates}, - expected_status=200) + body={'region': region_updates}) # NOTE(dstanek): Keystone should keep the original description. self.assertEqual(region_ref['description'], @@ -326,19 +305,22 @@ class CatalogTestCase(test_v3.RestfulTestCase): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'True' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_service_enabled_str_false(self): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'False' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_service_enabled_str_random(self): """Call ``POST /services``.""" ref = self.new_service_ref() ref['enabled'] = 'puppies' - self.post('/services', body={'service': ref}, expected_status=400) + self.post('/services', body={'service': ref}, + expected_status=http_client.BAD_REQUEST) def test_list_services(self): """Call ``GET /services``.""" @@ -575,7 +557,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_enabled_str_false(self): """Call ``POST /endpoints`` with enabled: 'False'.""" @@ -584,7 +566,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_enabled_str_random(self): """Call ``POST /endpoints`` with enabled: 'puppies'.""" @@ -593,13 +575,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.post( '/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_with_invalid_region_id(self): """Call ``POST /endpoints``.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref["region_id"] = uuid.uuid4().hex - self.post('/endpoints', body={'endpoint': ref}, expected_status=400) + self.post('/endpoints', body={'endpoint': ref}, + expected_status=http_client.BAD_REQUEST) def test_create_endpoint_with_region(self): """EndpointV3 creates the region before creating the endpoint, if @@ -608,7 +591,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_endpoint_ref(service_id=self.service_id) ref["region"] = uuid.uuid4().hex ref.pop('region_id') - self.post('/endpoints', body={'endpoint': ref}, expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) # Make sure the region is created self.get('/regions/%(region_id)s' % { 'region_id': ref["region"]}) @@ -617,13 +600,14 @@ class CatalogTestCase(test_v3.RestfulTestCase): """EndpointV3 allows to creates the endpoint without region.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref.pop('region_id') - self.post('/endpoints', body={'endpoint': ref}, expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) def test_create_endpoint_with_empty_url(self): """Call ``POST /endpoints``.""" ref = self.new_endpoint_ref(service_id=self.service_id) ref["url"] = '' - self.post('/endpoints', body={'endpoint': ref}, expected_status=400) + self.post('/endpoints', body={'endpoint': ref}, + expected_status=http_client.BAD_REQUEST) def test_get_endpoint(self): """Call ``GET /endpoints/{endpoint_id}``.""" @@ -667,7 +651,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'True'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_endpoint_enabled_str_false(self): """Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'False'.""" @@ -675,7 +659,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'False'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_endpoint_enabled_str_random(self): """Call ``PATCH /endpoints/{endpoint_id}`` with enabled: 'kitties'.""" @@ -683,7 +667,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): '/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint_id}, body={'endpoint': {'enabled': 'kitties'}}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_delete_endpoint(self): """Call ``DELETE /endpoints/{endpoint_id}``.""" @@ -762,7 +746,8 @@ class CatalogTestCase(test_v3.RestfulTestCase): self.delete('/endpoints/%s' % ref['id']) # make sure it's deleted (GET should return 404) - self.get('/endpoints/%s' % ref['id'], expected_status=404) + self.get('/endpoints/%s' % ref['id'], + expected_status=http_client.NOT_FOUND) def test_endpoint_create_with_valid_url(self): """Create endpoint with valid url should be tested,too.""" @@ -771,9 +756,7 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref = self.new_endpoint_ref(self.service_id) ref['url'] = valid_url - self.post('/endpoints', - body={'endpoint': ref}, - expected_status=201) + self.post('/endpoints', body={'endpoint': ref}) def test_endpoint_create_with_invalid_url(self): """Test the invalid cases: substitutions is not exactly right. @@ -798,10 +781,10 @@ class CatalogTestCase(test_v3.RestfulTestCase): ref['url'] = invalid_url self.post('/endpoints', body={'endpoint': ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) -class TestCatalogAPISQL(tests.TestCase): +class TestCatalogAPISQL(unit.TestCase): """Tests for the catalog Manager against the SQL backend. """ @@ -909,7 +892,7 @@ class TestCatalogAPISQL(tests.TestCase): # TODO(dstanek): this needs refactoring with the test above, but we are in a # crunch so that will happen in a future patch. -class TestCatalogAPISQLRegions(tests.TestCase): +class TestCatalogAPISQLRegions(unit.TestCase): """Tests for the catalog Manager against the SQL backend. """ diff --git a/keystone-moon/keystone/tests/unit/test_v3_controller.py b/keystone-moon/keystone/tests/unit/test_v3_controller.py index eef64a82..563e656e 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_controller.py +++ b/keystone-moon/keystone/tests/unit/test_v3_controller.py @@ -20,10 +20,10 @@ from testtools import matchers from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit -class V3ControllerTestCase(tests.TestCase): +class V3ControllerTestCase(unit.TestCase): """Tests for the V3Controller class.""" def setUp(self): super(V3ControllerTestCase, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/test_v3_credential.py b/keystone-moon/keystone/tests/unit/test_v3_credential.py index f8f6d35b..cf504b00 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_credential.py +++ b/keystone-moon/keystone/tests/unit/test_v3_credential.py @@ -18,6 +18,7 @@ import uuid from keystoneclient.contrib.ec2 import utils as ec2_utils from oslo_config import cfg +from six.moves import http_client from testtools import matchers from keystone import exception @@ -98,6 +99,60 @@ class CredentialTestCase(CredentialBaseTestCase): for cred in r.result['credentials']: self.assertEqual(self.user['id'], cred['user_id']) + def test_list_credentials_filtered_by_type(self): + """Call ``GET /credentials?type={type}``.""" + # The type ec2 was chosen, instead of a random string, + # because the type must be in the list of supported types + ec2_credential = self.new_credential_ref(user_id=uuid.uuid4().hex, + project_id=self.project_id, + cred_type='ec2') + + ec2_resp = self.credential_api.create_credential( + ec2_credential['id'], ec2_credential) + + # The type cert was chosen for the same reason as ec2 + r = self.get('/credentials?type=cert') + + # Testing the filter for two different types + self.assertValidCredentialListResponse(r, ref=self.credential) + for cred in r.result['credentials']: + self.assertEqual('cert', cred['type']) + + r_ec2 = self.get('/credentials?type=ec2') + self.assertThat(r_ec2.result['credentials'], matchers.HasLength(1)) + cred_ec2 = r_ec2.result['credentials'][0] + + self.assertValidCredentialListResponse(r_ec2, ref=ec2_resp) + self.assertEqual('ec2', cred_ec2['type']) + self.assertEqual(cred_ec2['id'], ec2_credential['id']) + + def test_list_credentials_filtered_by_type_and_user_id(self): + """Call ``GET /credentials?user_id={user_id}&type={type}``.""" + user1_id = uuid.uuid4().hex + user2_id = uuid.uuid4().hex + + # Creating credentials for two different users + credential_user1_ec2 = self.new_credential_ref( + user_id=user1_id, cred_type='ec2') + credential_user1_cert = self.new_credential_ref( + user_id=user1_id) + credential_user2_cert = self.new_credential_ref( + user_id=user2_id) + + self.credential_api.create_credential( + credential_user1_ec2['id'], credential_user1_ec2) + self.credential_api.create_credential( + credential_user1_cert['id'], credential_user1_cert) + self.credential_api.create_credential( + credential_user2_cert['id'], credential_user2_cert) + + r = self.get('/credentials?user_id=%s&type=ec2' % user1_id) + self.assertValidCredentialListResponse(r, ref=credential_user1_ec2) + self.assertThat(r.result['credentials'], matchers.HasLength(1)) + cred = r.result['credentials'][0] + self.assertEqual('ec2', cred['type']) + self.assertEqual(user1_id, cred['user_id']) + def test_create_credential(self): """Call ``POST /credentials``.""" ref = self.new_credential_ref(user_id=self.user['id']) @@ -198,10 +253,10 @@ class CredentialTestCase(CredentialBaseTestCase): "secret": uuid.uuid4().hex} ref['blob'] = json.dumps(blob) ref['type'] = 'ec2' - # Assert 400 status for bad request with missing project_id + # Assert bad request status when missing project_id self.post( '/credentials', - body={'credential': ref}, expected_status=400) + body={'credential': ref}, expected_status=http_client.BAD_REQUEST) def test_create_ec2_credential_with_invalid_blob(self): """Call ``POST /credentials`` for creating ec2 @@ -211,11 +266,10 @@ class CredentialTestCase(CredentialBaseTestCase): project_id=self.project_id) ref['blob'] = '{"abc":"def"d}' ref['type'] = 'ec2' - # Assert 400 status for bad request containing invalid - # blob + # Assert bad request status when request contains invalid blob response = self.post( '/credentials', - body={'credential': ref}, expected_status=400) + body={'credential': ref}, expected_status=http_client.BAD_REQUEST) self.assertValidErrorResponse(response) def test_create_credential_with_admin_token(self): @@ -328,7 +382,7 @@ class TestCredentialEc2(CredentialBaseTestCase): r = self.post( '/ec2tokens', body={'ec2Credentials': sig_ref}, - expected_status=200) + expected_status=http_client.OK) self.assertValidTokenResponse(r) def test_ec2_credential_signature_validate(self): diff --git a/keystone-moon/keystone/tests/unit/test_v3_domain_config.py b/keystone-moon/keystone/tests/unit/test_v3_domain_config.py index 6f96f0e7..3f7af87d 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_domain_config.py +++ b/keystone-moon/keystone/tests/unit/test_v3_domain_config.py @@ -14,6 +14,7 @@ import copy import uuid from oslo_config import cfg +from six.moves import http_client from keystone import exception from keystone.tests.unit import test_v3 @@ -39,7 +40,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): url = '/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']} r = self.put(url, body={'config': self.config}, - expected_status=201) + expected_status=http_client.CREATED) res = self.domain_config_api.get_config(self.domain['id']) self.assertEqual(self.config, r.result['config']) self.assertEqual(self.config, res) @@ -49,11 +50,11 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.put('/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']}, body={'config': self.config}, - expected_status=201) + expected_status=http_client.CREATED) self.put('/domains/%(domain_id)s/config' % { 'domain_id': self.domain['id']}, body={'config': self.config}, - expected_status=200) + expected_status=http_client.OK) def test_delete_config(self): """Call ``DELETE /domains{domain_id}/config``.""" @@ -79,7 +80,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id']} r = self.get(url) self.assertEqual(self.config, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_config_by_group(self): """Call ``GET & HEAD /domains{domain_id}/config/{group}``.""" @@ -88,7 +89,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id']} r = self.get(url) self.assertEqual({'ldap': self.config['ldap']}, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_config_by_option(self): """Call ``GET & HEAD /domains{domain_id}/config/{group}/{option}``.""" @@ -98,26 +99,29 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): r = self.get(url) self.assertEqual({'url': self.config['ldap']['url']}, r.result['config']) - self.head(url, expected_status=200) + self.head(url, expected_status=http_client.OK) def test_get_non_existant_config(self): """Call ``GET /domains{domain_id}/config when no config defined``.""" self.get('/domains/%(domain_id)s/config' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_get_non_existant_config_group(self): """Call ``GET /domains{domain_id}/config/{group_not_exist}``.""" config = {'ldap': {'url': uuid.uuid4().hex}} self.domain_config_api.create_config(self.domain['id'], config) self.get('/domains/%(domain_id)s/config/identity' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_get_non_existant_config_option(self): """Call ``GET /domains{domain_id}/config/group/{option_not_exist}``.""" config = {'ldap': {'url': uuid.uuid4().hex}} self.domain_config_api.create_config(self.domain['id'], config) self.get('/domains/%(domain_id)s/config/ldap/user_tree_dn' % { - 'domain_id': self.domain['id']}, expected_status=404) + 'domain_id': self.domain['id']}, + expected_status=http_client.NOT_FOUND) def test_update_config(self): """Call ``PATCH /domains/{domain_id}/config``.""" @@ -163,7 +167,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.patch('/domains/%(domain_id)s/config/%(invalid_group)s' % { 'domain_id': self.domain['id'], 'invalid_group': invalid_group}, body={'config': new_config}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Trying to update a valid group, but one that is not in the current # config should result in NotFound config = {'ldap': {'suffix': uuid.uuid4().hex}} @@ -172,7 +176,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): self.patch('/domains/%(domain_id)s/config/identity' % { 'domain_id': self.domain['id']}, body={'config': new_config}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_config_option(self): """Call ``PATCH /domains/{domain_id}/config/{group}/{option}``.""" @@ -199,7 +203,7 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): 'domain_id': self.domain['id'], 'invalid_option': invalid_option}, body={'config': new_config}, - expected_status=403) + expected_status=http_client.FORBIDDEN) # Trying to update a valid option, but one that is not in the current # config should result in NotFound new_config = {'suffix': uuid.uuid4().hex} @@ -207,4 +211,4 @@ class DomainConfigTestCase(test_v3.RestfulTestCase): '/domains/%(domain_id)s/config/ldap/suffix' % { 'domain_id': self.domain['id']}, body={'config': new_config}, - expected_status=404) + expected_status=http_client.NOT_FOUND) diff --git a/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py b/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py index 4daeff4d..b0c8256e 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py +++ b/keystone-moon/keystone/tests/unit/test_v3_endpoint_policy.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +from six.moves import http_client from testtools import matchers from keystone.tests.unit import test_v3 @@ -48,17 +49,23 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): # Test when the resource does not exist also ensures # that there is not a false negative after creation. - self.assert_head_and_get_return_same_response(url, expected_status=404) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NOT_FOUND) - self.put(url, expected_status=204) + self.put(url) # test that the new resource is accessible. - self.assert_head_and_get_return_same_response(url, expected_status=204) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NO_CONTENT) - self.delete(url, expected_status=204) + self.delete(url) # test that the deleted resource is no longer accessible - self.assert_head_and_get_return_same_response(url, expected_status=404) + self.assert_head_and_get_return_same_response( + url, + expected_status=http_client.NOT_FOUND) def test_crud_for_policy_for_explicit_endpoint(self): """PUT, HEAD and DELETE for explicit endpoint policy.""" @@ -94,18 +101,16 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): self.put('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints/%(endpoint_id)s' % { 'policy_id': self.policy['id'], - 'endpoint_id': self.endpoint['id']}, - expected_status=204) + 'endpoint_id': self.endpoint['id']}) self.head('/endpoints/%(endpoint_id)s/OS-ENDPOINT-POLICY' '/policy' % { 'endpoint_id': self.endpoint['id']}, - expected_status=200) + expected_status=http_client.OK) r = self.get('/endpoints/%(endpoint_id)s/OS-ENDPOINT-POLICY' '/policy' % { - 'endpoint_id': self.endpoint['id']}, - expected_status=200) + 'endpoint_id': self.endpoint['id']}) self.assertValidPolicyResponse(r, ref=self.policy) def test_list_endpoints_for_policy(self): @@ -114,13 +119,11 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): self.put('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints/%(endpoint_id)s' % { 'policy_id': self.policy['id'], - 'endpoint_id': self.endpoint['id']}, - expected_status=204) + 'endpoint_id': self.endpoint['id']}) r = self.get('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' '/endpoints' % { - 'policy_id': self.policy['id']}, - expected_status=200) + 'policy_id': self.policy['id']}) self.assertValidEndpointListResponse(r, ref=self.endpoint) self.assertThat(r.result.get('endpoints'), matchers.HasLength(1)) @@ -130,13 +133,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'endpoint_id': self.endpoint['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/endpoints/%(endpoint_id)s' % { 'endpoint_id': self.endpoint['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_region_service_association_cleanup_when_region_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -145,13 +148,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'service_id': self.service['id'], 'region_id': self.region['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/regions/%(region_id)s' % { 'region_id': self.region['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_region_service_association_cleanup_when_service_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -160,13 +163,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'service_id': self.service['id'], 'region_id': self.region['id']} - self.put(url, expected_status=204) - self.head(url, expected_status=204) + self.put(url) + self.head(url) self.delete('/services/%(service_id)s' % { 'service_id': self.service['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_service_association_cleanup_when_service_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -174,13 +177,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'service_id': self.service['id']} - self.put(url, expected_status=204) - self.get(url, expected_status=204) + self.put(url) + self.get(url, expected_status=http_client.NO_CONTENT) self.delete('/policies/%(policy_id)s' % { 'policy_id': self.policy['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) def test_service_association_cleanup_when_policy_deleted(self): url = ('/policies/%(policy_id)s/OS-ENDPOINT-POLICY' @@ -188,13 +191,13 @@ class EndpointPolicyTestCase(test_v3.RestfulTestCase): 'policy_id': self.policy['id'], 'service_id': self.service['id']} - self.put(url, expected_status=204) - self.get(url, expected_status=204) + self.put(url) + self.get(url, expected_status=http_client.NO_CONTENT) self.delete('/services/%(service_id)s' % { 'service_id': self.service['id']}) - self.head(url, expected_status=404) + self.head(url, expected_status=http_client.NOT_FOUND) class JsonHomeTests(test_v3.JsonHomeTestMixin): diff --git a/keystone-moon/keystone/tests/unit/test_v3_federation.py b/keystone-moon/keystone/tests/unit/test_v3_federation.py index e646bc0a..5717e67b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_federation.py +++ b/keystone-moon/keystone/tests/unit/test_v3_federation.py @@ -12,7 +12,6 @@ import os import random -import subprocess from testtools import matchers import uuid @@ -26,12 +25,14 @@ from oslotest import mockpatch import saml2 from saml2 import saml from saml2 import sigver +from six.moves import http_client from six.moves import range, urllib, zip xmldsig = importutils.try_import("saml2.xmldsig") if not xmldsig: xmldsig = importutils.try_import("xmldsig") from keystone.auth import controllers as auth_controllers +from keystone.common import environment from keystone.contrib.federation import controllers as federation_controllers from keystone.contrib.federation import idp as keystone_idp from keystone import exception @@ -44,6 +45,8 @@ from keystone.tests.unit import test_v3 from keystone.token.providers import common as token_common +subprocess = environment.subprocess + CONF = cfg.CONF LOG = log.getLogger(__name__) ROOTDIR = os.path.dirname(os.path.abspath(__file__)) @@ -109,25 +112,43 @@ class FederatedSetupMixin(object): self.assertEqual(token_projects, projects_ref) def _check_scoped_token_attributes(self, token): - def xor_project_domain(token_keys): - return sum(('project' in token_keys, 'domain' in token_keys)) % 2 for obj in ('user', 'catalog', 'expires_at', 'issued_at', 'methods', 'roles'): self.assertIn(obj, token) - # Check for either project or domain - if not xor_project_domain(list(token.keys())): - raise AssertionError("You must specify either" - "project or domain.") - self.assertIn('OS-FEDERATION', token['user']) os_federation = token['user']['OS-FEDERATION'] + + self.assertIn('groups', os_federation) + self.assertIn('identity_provider', os_federation) + self.assertIn('protocol', os_federation) + self.assertThat(os_federation, matchers.HasLength(3)) + self.assertEqual(self.IDP, os_federation['identity_provider']['id']) self.assertEqual(self.PROTOCOL, os_federation['protocol']['id']) - self.assertListEqual(sorted(['groups', - 'identity_provider', - 'protocol']), - sorted(os_federation.keys())) + + def _check_project_scoped_token_attributes(self, token, project_id): + self.assertEqual(project_id, token['project']['id']) + self._check_scoped_token_attributes(token) + + def _check_domain_scoped_token_attributes(self, token, domain_id): + self.assertEqual(domain_id, token['domain']['id']) + self._check_scoped_token_attributes(token) + + def assertValidMappedUser(self, token): + """Check if user object meets all the criteria.""" + + user = token['user'] + self.assertIn('id', user) + self.assertIn('name', user) + self.assertIn('domain', user) + + self.assertIn('groups', user['OS-FEDERATION']) + self.assertIn('identity_provider', user['OS-FEDERATION']) + self.assertIn('protocol', user['OS-FEDERATION']) + + # Make sure user_id is url safe + self.assertEqual(urllib.parse.quote(user['name']), user['id']) def _issue_unscoped_token(self, idp=None, @@ -794,7 +815,7 @@ class FederatedIdentityProviderTests(FederationTests): if body is None: body = self._http_idp_input() resp = self.put(url, body={'identity_provider': body}, - expected_status=201) + expected_status=http_client.CREATED) return resp def _http_idp_input(self, **kwargs): @@ -881,7 +902,7 @@ class FederatedIdentityProviderTests(FederationTests): body['remote_ids'] = [uuid.uuid4().hex, repeated_remote_id] self.put(url, body={'identity_provider': body}, - expected_status=409) + expected_status=http_client.CONFLICT) def test_create_idp_remote_empty(self): """Creates an IdP with empty remote_ids.""" @@ -1006,9 +1027,9 @@ class FederatedIdentityProviderTests(FederationTests): url = self.base_url(suffix=uuid.uuid4().hex) body = self._http_idp_input() self.put(url, body={'identity_provider': body}, - expected_status=201) + expected_status=http_client.CREATED) self.put(url, body={'identity_provider': body}, - expected_status=409) + expected_status=http_client.CONFLICT) def test_get_idp(self): """Create and later fetch IdP.""" @@ -1033,7 +1054,7 @@ class FederatedIdentityProviderTests(FederationTests): self.assertIsNotNone(idp_id) url = self.base_url(suffix=idp_id) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_existing_idp(self): """Create and later delete IdP. @@ -1047,7 +1068,7 @@ class FederatedIdentityProviderTests(FederationTests): self.assertIsNotNone(idp_id) url = self.base_url(suffix=idp_id) self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_delete_idp_also_deletes_assigned_protocols(self): """Deleting an IdP will delete its assigned protocol.""" @@ -1063,7 +1084,7 @@ class FederatedIdentityProviderTests(FederationTests): idp_url = self.base_url(suffix=idp_id) # assign protocol to IdP - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} resp, idp_id, proto = self._assign_protocol_to_idp( url=url, idp_id=idp_id, @@ -1073,7 +1094,7 @@ class FederatedIdentityProviderTests(FederationTests): # removing IdP will remove the assigned protocol as well self.assertEqual(1, len(self.federation_api.list_protocols(idp_id))) self.delete(idp_url) - self.get(idp_url, expected_status=404) + self.get(idp_url, expected_status=http_client.NOT_FOUND) self.assertEqual(0, len(self.federation_api.list_protocols(idp_id))) def test_delete_nonexisting_idp(self): @@ -1083,7 +1104,7 @@ class FederatedIdentityProviderTests(FederationTests): """ idp_id = uuid.uuid4().hex url = self.base_url(suffix=idp_id) - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_update_idp_mutable_attributes(self): """Update IdP's mutable parameters.""" @@ -1124,7 +1145,7 @@ class FederatedIdentityProviderTests(FederationTests): def test_update_idp_immutable_attributes(self): """Update IdP's immutable parameters. - Expect HTTP 403 code. + Expect HTTP FORBIDDEN. """ default_resp = self._create_default_idp() @@ -1138,7 +1159,8 @@ class FederatedIdentityProviderTests(FederationTests): body['protocols'] = [uuid.uuid4().hex, uuid.uuid4().hex] url = self.base_url(suffix=idp_id) - self.patch(url, body={'identity_provider': body}, expected_status=403) + self.patch(url, body={'identity_provider': body}, + expected_status=http_client.FORBIDDEN) def test_update_nonexistent_idp(self): """Update nonexistent IdP @@ -1152,12 +1174,12 @@ class FederatedIdentityProviderTests(FederationTests): body['enabled'] = False body = {'identity_provider': body} - self.patch(url, body=body, expected_status=404) + self.patch(url, body=body, expected_status=http_client.NOT_FOUND) def test_assign_protocol_to_idp(self): """Assign a protocol to existing IdP.""" - self._assign_protocol_to_idp(expected_status=201) + self._assign_protocol_to_idp(expected_status=http_client.CREATED) def test_protocol_composite_pk(self): """Test whether Keystone let's add two entities with identical @@ -1171,7 +1193,7 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='%(idp_id)s/protocols/%(protocol_id)s') - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} self._assign_protocol_to_idp(proto='saml2', url=url, **kwargs) @@ -1187,10 +1209,10 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='%(idp_id)s/protocols/%(protocol_id)s') - kwargs = {'expected_status': 201} + kwargs = {'expected_status': http_client.CREATED} resp, idp_id, proto = self._assign_protocol_to_idp(proto='saml2', url=url, **kwargs) - kwargs = {'expected_status': 409} + kwargs = {'expected_status': http_client.CONFLICT} resp, idp_id, proto = self._assign_protocol_to_idp(idp_id=idp_id, proto='saml2', validate=False, @@ -1204,7 +1226,7 @@ class FederatedIdentityProviderTests(FederationTests): """ idp_id = uuid.uuid4().hex - kwargs = {'expected_status': 404} + kwargs = {'expected_status': http_client.NOT_FOUND} self._assign_protocol_to_idp(proto='saml2', idp_id=idp_id, validate=False, @@ -1213,7 +1235,8 @@ class FederatedIdentityProviderTests(FederationTests): def test_get_protocol(self): """Create and later fetch protocol tied to IdP.""" - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) proto_id = self._fetch_attribute_from_response(resp, 'protocol')['id'] url = "%s/protocols/%s" % (idp_id, proto_id) url = self.base_url(suffix=url) @@ -1232,12 +1255,14 @@ class FederatedIdentityProviderTests(FederationTests): Compare input and output id sets. """ - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) iterations = random.randint(0, 16) protocol_ids = [] for _ in range(iterations): - resp, _, proto = self._assign_protocol_to_idp(idp_id=idp_id, - expected_status=201) + resp, _, proto = self._assign_protocol_to_idp( + idp_id=idp_id, + expected_status=http_client.CREATED) proto_id = self._fetch_attribute_from_response(resp, 'protocol') proto_id = proto_id['id'] protocol_ids.append(proto_id) @@ -1256,7 +1281,8 @@ class FederatedIdentityProviderTests(FederationTests): def test_update_protocols_attribute(self): """Update protocol's attribute.""" - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) new_mapping_id = uuid.uuid4().hex url = "%s/protocols/%s" % (idp_id, proto) @@ -1277,11 +1303,12 @@ class FederatedIdentityProviderTests(FederationTests): """ url = self.base_url(suffix='/%(idp_id)s/' 'protocols/%(protocol_id)s') - resp, idp_id, proto = self._assign_protocol_to_idp(expected_status=201) + resp, idp_id, proto = self._assign_protocol_to_idp( + expected_status=http_client.CREATED) url = url % {'idp_id': idp_id, 'protocol_id': proto} self.delete(url) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) class MappingCRUDTests(FederationTests): @@ -1318,7 +1345,7 @@ class MappingCRUDTests(FederationTests): url = self.MAPPING_URL + uuid.uuid4().hex resp = self.put(url, body={'mapping': mapping_fixtures.MAPPING_LARGE}, - expected_status=201) + expected_status=http_client.CREATED) return resp def _get_id_from_response(self, resp): @@ -1335,7 +1362,7 @@ class MappingCRUDTests(FederationTests): resp = self.get(url) entities = resp.result.get('mappings') self.assertIsNotNone(entities) - self.assertResponseStatus(resp, 200) + self.assertResponseStatus(resp, http_client.OK) self.assertValidListLinks(resp.result.get('links')) self.assertEqual(1, len(entities)) @@ -1345,8 +1372,8 @@ class MappingCRUDTests(FederationTests): mapping_id = self._get_id_from_response(resp) url = url % {'mapping_id': str(mapping_id)} resp = self.delete(url) - self.assertResponseStatus(resp, 204) - self.get(url, expected_status=404) + self.assertResponseStatus(resp, http_client.NO_CONTENT) + self.get(url, expected_status=http_client.NOT_FOUND) def test_mapping_get(self): url = self.MAPPING_URL + '%(mapping_id)s' @@ -1369,70 +1396,73 @@ class MappingCRUDTests(FederationTests): def test_delete_mapping_dne(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) def test_get_mapping_dne(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_create_mapping_bad_requirements(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_BAD_REQ}) def test_create_mapping_no_rules(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_NO_RULES}) def test_create_mapping_no_remote_objects(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_NO_REMOTE}) def test_create_mapping_bad_value(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_BAD_VALUE}) def test_create_mapping_missing_local(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_MISSING_LOCAL}) def test_create_mapping_missing_type(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_MISSING_TYPE}) def test_create_mapping_wrong_type(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_WRONG_TYPE}) def test_create_mapping_extra_remote_properties_not_any_of(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_NOT_ANY_OF - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_extra_remote_properties_any_one_of(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_ANY_ONE_OF - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_extra_remote_properties_just_type(self): url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_EXTRA_REMOTE_PROPS_JUST_TYPE - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) def test_create_mapping_empty_map(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': {}}) def test_create_mapping_extra_rules_properties(self): url = self.MAPPING_URL + uuid.uuid4().hex - self.put(url, expected_status=400, + self.put(url, expected_status=http_client.BAD_REQUEST, body={'mapping': mapping_fixtures.MAPPING_EXTRA_RULES_PROPS}) def test_create_mapping_with_blacklist_and_whitelist(self): @@ -1444,7 +1474,8 @@ class MappingCRUDTests(FederationTests): """ url = self.MAPPING_URL + uuid.uuid4().hex mapping = mapping_fixtures.MAPPING_GROUPS_WHITELIST_AND_BLACKLIST - self.put(url, expected_status=400, body={'mapping': mapping}) + self.put(url, expected_status=http_client.BAD_REQUEST, + body={'mapping': mapping}) class FederatedTokenTests(FederationTests, FederatedSetupMixin): @@ -1494,6 +1525,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): def test_issue_unscoped_token(self): r = self._issue_unscoped_token() self.assertIsNotNone(r.headers.get('X-Subject-Token')) + self.assertValidMappedUser(r.json['token']) def test_issue_unscoped_token_disabled_idp(self): """Checks if authentication works with disabled identity providers. @@ -1632,11 +1664,12 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_EMPLOYEE) token_resp = r.result['token'] project_id = token_resp['project']['id'] - self.assertEqual(project_id, self.proj_employees['id']) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project_id) roles_ref = [self.role_employee] + projects_ref = self.proj_employees self._check_projects_and_roles(token_resp, roles_ref, projects_ref) + self.assertValidMappedUser(token_resp) def test_scope_token_with_idp_disabled(self): """Scope token issued by disabled IdP. @@ -1659,14 +1692,14 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): self.federation_api.update_idp(self.IDP, enabled_false) self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_CUSTOMER, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_scope_to_bad_project(self): """Scope unscoped token with a project we don't have access to.""" self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_EMPLOYEE_FROM_CUSTOMER, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_scope_to_project_multiple_times(self): """Try to scope the unscoped token multiple times. @@ -1685,9 +1718,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): for body, project_id_ref in zip(bodies, project_ids): r = self.v3_authenticate_token(body) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project_id, project_id_ref) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, + project_id_ref) def test_scope_to_project_with_only_inherited_roles(self): """Try to scope token whose only roles are inherited.""" @@ -1695,18 +1727,18 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): r = self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_INHERITED_FROM_CUSTOMER) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project_id, self.project_inherited['id']) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes( + token_resp, self.project_inherited['id']) roles_ref = [self.role_customer] projects_ref = self.project_inherited self._check_projects_and_roles(token_resp, roles_ref, projects_ref) + self.assertValidMappedUser(token_resp) def test_scope_token_from_nonexistent_unscoped_token(self): """Try to scope token from non-existent unscoped token.""" self.v3_authenticate_token( self.TOKEN_SCOPE_PROJECT_FROM_NONEXISTENT_TOKEN, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_issue_token_from_rules_without_user(self): api = auth_controllers.Auth() @@ -1730,9 +1762,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): def test_scope_to_domain_once(self): r = self.v3_authenticate_token(self.TOKEN_SCOPE_DOMAIN_A_FROM_CUSTOMER) token_resp = r.result['token'] - domain_id = token_resp['domain']['id'] - self.assertEqual(self.domainA['id'], domain_id) - self._check_scoped_token_attributes(token_resp) + self._check_domain_scoped_token_attributes(token_resp, + self.domainA['id']) def test_scope_to_domain_multiple_tokens(self): """Issue multiple tokens scoping to different domains. @@ -1754,15 +1785,14 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): for body, domain_id_ref in zip(bodies, domain_ids): r = self.v3_authenticate_token(body) token_resp = r.result['token'] - domain_id = token_resp['domain']['id'] - self.assertEqual(domain_id_ref, domain_id) - self._check_scoped_token_attributes(token_resp) + self._check_domain_scoped_token_attributes(token_resp, + domain_id_ref) def test_scope_to_domain_with_only_inherited_roles_fails(self): """Try to scope to a domain that has no direct roles.""" self.v3_authenticate_token( self.TOKEN_SCOPE_DOMAIN_D_FROM_CUSTOMER, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_list_projects(self): urls = ('/OS-FEDERATION/projects', '/auth/projects') @@ -1863,9 +1893,10 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): """ r = self._issue_unscoped_token() + token_resp = r.json_body['token'] + self.assertValidMappedUser(token_resp) employee_unscoped_token_id = r.headers.get('X-Subject-Token') - r = self.get('/OS-FEDERATION/projects', - token=employee_unscoped_token_id) + r = self.get('/auth/projects', token=employee_unscoped_token_id) projects = r.result['projects'] random_project = random.randint(0, len(projects)) - 1 project = projects[random_project] @@ -1875,9 +1906,7 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): r = self.v3_authenticate_token(v3_scope_request) token_resp = r.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project['id'], project_id) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project['id']) def test_workflow_with_groups_deletion(self): """Test full workflow with groups deletion before token scoping. @@ -1947,7 +1976,8 @@ class FederatedTokenTests(FederationTests, FederatedSetupMixin): token_id, 'project', self.project_all['id']) - self.v3_authenticate_token(scoped_token, expected_status=500) + self.v3_authenticate_token( + scoped_token, expected_status=http_client.INTERNAL_SERVER_ERROR) def test_lists_with_missing_group_in_backend(self): """Test a mapping that points to a group that does not exist @@ -2379,11 +2409,13 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): def test_federated_unscoped_token(self): resp = self._issue_unscoped_token() self.assertEqual(204, len(resp.headers['X-Subject-Token'])) + self.assertValidMappedUser(resp.json_body['token']) def test_federated_unscoped_token_with_multiple_groups(self): assertion = 'ANOTHER_CUSTOMER_ASSERTION' resp = self._issue_unscoped_token(assertion=assertion) - self.assertEqual(232, len(resp.headers['X-Subject-Token'])) + self.assertEqual(226, len(resp.headers['X-Subject-Token'])) + self.assertValidMappedUser(resp.json_body['token']) def test_validate_federated_unscoped_token(self): resp = self._issue_unscoped_token() @@ -2400,9 +2432,9 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): """ resp = self._issue_unscoped_token() + self.assertValidMappedUser(resp.json_body['token']) unscoped_token = resp.headers.get('X-Subject-Token') - resp = self.get('/OS-FEDERATION/projects', - token=unscoped_token) + resp = self.get('/auth/projects', token=unscoped_token) projects = resp.result['projects'] random_project = random.randint(0, len(projects)) - 1 project = projects[random_project] @@ -2412,9 +2444,7 @@ class FernetFederatedTokenTests(FederationTests, FederatedSetupMixin): resp = self.v3_authenticate_token(v3_scope_request) token_resp = resp.result['token'] - project_id = token_resp['project']['id'] - self.assertEqual(project['id'], project_id) - self._check_scoped_token_attributes(token_resp) + self._check_project_scoped_token_attributes(token_resp, project['id']) class FederatedTokenTestsMethodToken(FederatedTokenTests): @@ -2499,7 +2529,7 @@ class SAMLGenerationTests(FederationTests): self.sp = self.sp_ref() url = '/OS-FEDERATION/service_providers/' + self.SERVICE_PROVDIER_ID self.put(url, body={'service_provider': self.sp}, - expected_status=201) + expected_status=http_client.CREATED) def test_samlize_token_values(self): """Test the SAML generator produces a SAML object. @@ -2615,8 +2645,8 @@ class SAMLGenerationTests(FederationTests): # the assertion as is without signing it return assertion_content - with mock.patch('subprocess.check_output', - side_effect=mocked_subprocess_check_output): + with mock.patch.object(subprocess, 'check_output', + side_effect=mocked_subprocess_check_output): generator = keystone_idp.SAMLGenerator() response = generator.samlize_token(self.ISSUER, self.RECIPIENT, self.SUBJECT, @@ -2711,7 +2741,7 @@ class SAMLGenerationTests(FederationTests): with mock.patch.object(keystone_idp, '_sign_assertion', return_value=self.signed_assertion): self.post(self.SAML_GENERATION_ROUTE, body=body, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_generate_saml_route(self): """Test that the SAML generation endpoint produces XML. @@ -2733,7 +2763,7 @@ class SAMLGenerationTests(FederationTests): return_value=self.signed_assertion): http_response = self.post(self.SAML_GENERATION_ROUTE, body=body, response_content_type='text/xml', - expected_status=200) + expected_status=http_client.OK) response = etree.fromstring(http_response.result) issuer = response[0] @@ -2774,7 +2804,8 @@ class SAMLGenerationTests(FederationTests): self.SERVICE_PROVDIER_ID) del body['auth']['scope'] - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=400) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.BAD_REQUEST) def test_invalid_token_body(self): """Test that missing the token in request body raises an exception. @@ -2788,7 +2819,8 @@ class SAMLGenerationTests(FederationTests): self.SERVICE_PROVDIER_ID) del body['auth']['identity']['token'] - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=400) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.BAD_REQUEST) def test_sp_not_found(self): """Test SAML generation with an invalid service provider ID. @@ -2799,7 +2831,8 @@ class SAMLGenerationTests(FederationTests): sp_id = uuid.uuid4().hex token_id = self._fetch_valid_token() body = self._create_generate_saml_request(token_id, sp_id) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=404) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.NOT_FOUND) def test_sp_disabled(self): """Try generating assertion for disabled Service Provider.""" @@ -2811,7 +2844,8 @@ class SAMLGenerationTests(FederationTests): token_id = self._fetch_valid_token() body = self._create_generate_saml_request(token_id, self.SERVICE_PROVDIER_ID) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=403) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.FORBIDDEN) def test_token_not_found(self): """Test that an invalid token in the request body raises an exception. @@ -2823,7 +2857,8 @@ class SAMLGenerationTests(FederationTests): token_id = uuid.uuid4().hex body = self._create_generate_saml_request(token_id, self.SERVICE_PROVDIER_ID) - self.post(self.SAML_GENERATION_ROUTE, body=body, expected_status=404) + self.post(self.SAML_GENERATION_ROUTE, body=body, + expected_status=http_client.NOT_FOUND) def test_generate_ecp_route(self): """Test that the ECP generation endpoint produces XML. @@ -2844,7 +2879,7 @@ class SAMLGenerationTests(FederationTests): return_value=self.signed_assertion): http_response = self.post(self.ECP_GENERATION_ROUTE, body=body, response_content_type='text/xml', - expected_status=200) + expected_status=http_client.OK) env_response = etree.fromstring(http_response.result) header = env_response[0] @@ -2879,7 +2914,7 @@ class SAMLGenerationTests(FederationTests): @mock.patch('saml2.create_class_from_xml_string') @mock.patch('oslo_utils.fileutils.write_to_tempfile') - @mock.patch('subprocess.check_output') + @mock.patch.object(subprocess, 'check_output') def test__sign_assertion(self, check_output_mock, write_to_tempfile_mock, create_class_mock): write_to_tempfile_mock.return_value = 'tmp_path' @@ -2890,7 +2925,7 @@ class SAMLGenerationTests(FederationTests): create_class_mock.assert_called_with(saml.Assertion, 'fakeoutput') @mock.patch('oslo_utils.fileutils.write_to_tempfile') - @mock.patch('subprocess.check_output') + @mock.patch.object(subprocess, 'check_output') def test__sign_assertion_exc(self, check_output_mock, write_to_tempfile_mock): # If the command fails the command output is logged. @@ -2903,12 +2938,15 @@ class SAMLGenerationTests(FederationTests): returncode=sample_returncode, cmd=CONF.saml.xmlsec1_binary, output=sample_output) - # FIXME(blk-u): This should raise exception.SAMLSigningError instead, - # but fails with TypeError due to concatenating string to Message, see - # bug 1484735. - self.assertRaises(TypeError, + logger_fixture = self.useFixture(fixtures.LoggerFixture()) + self.assertRaises(exception.SAMLSigningError, keystone_idp._sign_assertion, self.signed_assertion) + expected_log = ( + "Error when signing assertion, reason: Command '%s' returned " + "non-zero exit status %s %s\n" % + (CONF.saml.xmlsec1_binary, sample_returncode, sample_output)) + self.assertEqual(expected_log, logger_fixture.output) @mock.patch('oslo_utils.fileutils.write_to_tempfile') def test__sign_assertion_fileutils_exc(self, write_to_tempfile_mock): @@ -3041,13 +3079,13 @@ class IdPMetadataGenerationTests(FederationTests): self.generator.generate_metadata) def test_get_metadata_with_no_metadata_file_configured(self): - self.get(self.METADATA_URL, expected_status=500) + self.get(self.METADATA_URL, + expected_status=http_client.INTERNAL_SERVER_ERROR) def test_get_metadata(self): self.config_fixture.config( group='saml', idp_metadata_path=XMLDIR + '/idp_saml2_metadata.xml') - r = self.get(self.METADATA_URL, response_content_type='text/xml', - expected_status=200) + r = self.get(self.METADATA_URL, response_content_type='text/xml') self.assertEqual('text/xml', r.headers.get('Content-Type')) reference_file = _load_xml('idp_saml2_metadata.xml') @@ -3070,7 +3108,7 @@ class ServiceProviderTests(FederationTests): self.SP_REF = self.sp_ref() self.SERVICE_PROVIDER = self.put( url, body={'service_provider': self.SP_REF}, - expected_status=201).result + expected_status=http_client.CREATED).result def sp_ref(self): ref = { @@ -3089,19 +3127,19 @@ class ServiceProviderTests(FederationTests): def test_get_service_provider(self): url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.get(url, expected_status=200) + resp = self.get(url) self.assertValidEntity(resp.result['service_provider'], keys_to_check=self.SP_KEYS) def test_get_service_provider_fail(self): url = self.base_url(suffix=uuid.uuid4().hex) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_create_service_provider(self): url = self.base_url(suffix=uuid.uuid4().hex) sp = self.sp_ref() resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) self.assertValidEntity(resp.result['service_provider'], keys_to_check=self.SP_KEYS) @@ -3111,7 +3149,7 @@ class ServiceProviderTests(FederationTests): sp = self.sp_ref() del sp['relay_state_prefix'] resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) sp_result = resp.result['service_provider'] self.assertEqual(CONF.saml.relay_state_prefix, sp_result['relay_state_prefix']) @@ -3123,7 +3161,7 @@ class ServiceProviderTests(FederationTests): non_default_prefix = uuid.uuid4().hex sp['relay_state_prefix'] = non_default_prefix resp = self.put(url, body={'service_provider': sp}, - expected_status=201) + expected_status=http_client.CREATED) sp_result = resp.result['service_provider'] self.assertEqual(non_default_prefix, sp_result['relay_state_prefix']) @@ -3134,7 +3172,7 @@ class ServiceProviderTests(FederationTests): sp = self.sp_ref() sp[uuid.uuid4().hex] = uuid.uuid4().hex self.put(url, body={'service_provider': sp}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_list_service_providers(self): """Test listing of service provider objects. @@ -3150,7 +3188,8 @@ class ServiceProviderTests(FederationTests): } for id, sp in ref_service_providers.items(): url = self.base_url(suffix=id) - self.put(url, body={'service_provider': sp}, expected_status=201) + self.put(url, body={'service_provider': sp}, + expected_status=http_client.CREATED) # Insert ids into service provider object, we will compare it with # responses from server and those include 'id' attribute. @@ -3177,15 +3216,14 @@ class ServiceProviderTests(FederationTests): """ new_sp_ref = self.sp_ref() url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=200) + resp = self.patch(url, body={'service_provider': new_sp_ref}) patch_result = resp.result new_sp_ref['id'] = self.SERVICE_PROVIDER_ID self.assertValidEntity(patch_result['service_provider'], ref=new_sp_ref, keys_to_check=self.SP_KEYS) - resp = self.get(url, expected_status=200) + resp = self.get(url) get_result = resp.result self.assertDictEqual(patch_result['service_provider'], @@ -3201,21 +3239,21 @@ class ServiceProviderTests(FederationTests): new_sp_ref = {'id': uuid.uuid4().hex} url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_service_provider_unknown_parameter(self): new_sp_ref = self.sp_ref() new_sp_ref[uuid.uuid4().hex] = uuid.uuid4().hex url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_update_service_provider_404(self): new_sp_ref = self.sp_ref() new_sp_ref['description'] = uuid.uuid4().hex url = self.base_url(suffix=uuid.uuid4().hex) self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_update_sp_relay_state(self): """Update an SP with custome relay state.""" @@ -3223,19 +3261,18 @@ class ServiceProviderTests(FederationTests): non_default_prefix = uuid.uuid4().hex new_sp_ref['relay_state_prefix'] = non_default_prefix url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - resp = self.patch(url, body={'service_provider': new_sp_ref}, - expected_status=200) + resp = self.patch(url, body={'service_provider': new_sp_ref}) sp_result = resp.result['service_provider'] self.assertEqual(non_default_prefix, sp_result['relay_state_prefix']) def test_delete_service_provider(self): url = self.base_url(suffix=self.SERVICE_PROVIDER_ID) - self.delete(url, expected_status=204) + self.delete(url) def test_delete_service_provider_404(self): url = self.base_url(suffix=uuid.uuid4().hex) - self.delete(url, expected_status=404) + self.delete(url, expected_status=http_client.NOT_FOUND) class WebSSOTests(FederatedTokenTests): @@ -3337,6 +3374,16 @@ class WebSSOTests(FederatedTokenTests): self.api.federated_sso_auth, context, self.PROTOCOL) + def test_identity_provider_specific_federated_authentication(self): + environment = {self.REMOTE_ID_ATTR: self.REMOTE_IDS[0]} + context = {'environment': environment} + query_string = {'origin': self.ORIGIN} + self._inject_assertion(context, 'EMPLOYEE_ASSERTION', query_string) + resp = self.api.federated_idp_specific_sso_auth(context, + self.idp['id'], + self.PROTOCOL) + self.assertIn(self.TRUSTED_DASHBOARD, resp.body) + class K2KServiceCatalogTests(FederationTests): SP1 = 'SP1' diff --git a/keystone-moon/keystone/tests/unit/test_v3_identity.py b/keystone-moon/keystone/tests/unit/test_v3_identity.py index e0090829..3d424cea 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_identity.py +++ b/keystone-moon/keystone/tests/unit/test_v3_identity.py @@ -16,12 +16,14 @@ import logging import uuid import fixtures +import mock from oslo_config import cfg +from six.moves import http_client from testtools import matchers from keystone.common import controller from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import test_v3 @@ -96,17 +98,24 @@ class IdentityTestCase(test_v3.RestfulTestCase): user_id=self.user['id'], password=self.user['password'], project_id=self.project['id']) - r = self.post('/users', body={'user': ref_nd}, auth=auth) + # TODO(henry-nash): Due to bug #1283539 we currently automatically # use the default domain_id if a domain scoped token is not being - # used. Change the code below to expect a failure once this bug is + # used. For now we just check that a deprecation warning has been + # issued. Change the code below to expect a failure once this bug is # fixed. + with mock.patch( + 'oslo_log.versionutils.report_deprecated_feature') as mock_dep: + r = self.post('/users', body={'user': ref_nd}, auth=auth) + self.assertTrue(mock_dep.called) + ref['domain_id'] = CONF.identity.default_domain_id return self.assertValidUserResponse(r, ref) - def test_create_user_400(self): + def test_create_user_bad_request(self): """Call ``POST /users``.""" - self.post('/users', body={'user': {}}, expected_status=400) + self.post('/users', body={'user': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_users(self): """Call ``GET /users``.""" @@ -286,30 +295,31 @@ class IdentityTestCase(test_v3.RestfulTestCase): old_password_auth = self.build_authentication_request( user_id=user_ref['id'], password=password) - r = self.v3_authenticate_token(old_password_auth, expected_status=201) + r = self.v3_authenticate_token(old_password_auth) old_token = r.headers.get('X-Subject-Token') # auth as user with a token should work before a password change old_token_auth = self.build_authentication_request(token=old_token) - self.v3_authenticate_token(old_token_auth, expected_status=201) + self.v3_authenticate_token(old_token_auth) # administrative password reset new_password = uuid.uuid4().hex self.patch('/users/%s' % user_ref['id'], - body={'user': {'password': new_password}}, - expected_status=200) + body={'user': {'password': new_password}}) # auth as user with original password should not work after change - self.v3_authenticate_token(old_password_auth, expected_status=401) + self.v3_authenticate_token(old_password_auth, + expected_status=http_client.UNAUTHORIZED) # auth as user with an old token should not work after change - self.v3_authenticate_token(old_token_auth, expected_status=404) + self.v3_authenticate_token(old_token_auth, + expected_status=http_client.NOT_FOUND) # new password should work new_password_auth = self.build_authentication_request( user_id=user_ref['id'], password=new_password) - self.v3_authenticate_token(new_password_auth, expected_status=201) + self.v3_authenticate_token(new_password_auth) def test_update_user_domain_id(self): """Call ``PATCH /users/{user_id}`` with domain_id.""" @@ -360,7 +370,7 @@ class IdentityTestCase(test_v3.RestfulTestCase): # Confirm token is valid for now self.head('/auth/tokens', headers={'X-Subject-Token': token}, - expected_status=200) + expected_status=http_client.OK) # Now delete the user self.delete('/users/%(user_id)s' % { @@ -389,9 +399,10 @@ class IdentityTestCase(test_v3.RestfulTestCase): body={'group': ref}) return self.assertValidGroupResponse(r, ref) - def test_create_group_400(self): + def test_create_group_bad_request(self): """Call ``POST /groups``.""" - self.post('/groups', body={'group': {}}, expected_status=400) + self.post('/groups', body={'group': {}}, + expected_status=http_client.BAD_REQUEST) def test_list_groups(self): """Call ``GET /groups``.""" @@ -462,14 +473,13 @@ class IdentityTestCase(test_v3.RestfulTestCase): # administrative password reset new_password = uuid.uuid4().hex self.patch('/users/%s' % user_ref['id'], - body={'user': {'password': new_password}}, - expected_status=200) + body={'user': {'password': new_password}}) self.assertNotIn(password, log_fix.output) self.assertNotIn(new_password, log_fix.output) -class IdentityV3toV2MethodsTestCase(tests.TestCase): +class IdentityV3toV2MethodsTestCase(unit.TestCase): """Test users V3 to V2 conversion methods.""" def setUp(self): @@ -549,7 +559,8 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): password = self.user_ref['password'] self.user_ref = self.identity_api.create_user(self.user_ref) self.user_ref['password'] = password - self.token = self.get_request_token(self.user_ref['password'], 201) + self.token = self.get_request_token(self.user_ref['password'], + http_client.CREATED) def get_request_token(self, password, expected_status): auth_data = self.build_authentication_request( @@ -569,42 +580,45 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): def test_changing_password(self): # original password works token_id = self.get_request_token(self.user_ref['password'], - expected_status=201) + expected_status=http_client.CREATED) # original token works old_token_auth = self.build_authentication_request(token=token_id) - self.v3_authenticate_token(old_token_auth, expected_status=201) + self.v3_authenticate_token(old_token_auth) # change password new_password = uuid.uuid4().hex self.change_password(password=new_password, original_password=self.user_ref['password'], - expected_status=204) + expected_status=http_client.NO_CONTENT) # old password fails - self.get_request_token(self.user_ref['password'], expected_status=401) + self.get_request_token(self.user_ref['password'], + expected_status=http_client.UNAUTHORIZED) # old token fails - self.v3_authenticate_token(old_token_auth, expected_status=404) + self.v3_authenticate_token(old_token_auth, + expected_status=http_client.NOT_FOUND) # new password works - self.get_request_token(new_password, expected_status=201) + self.get_request_token(new_password, + expected_status=http_client.CREATED) def test_changing_password_with_missing_original_password_fails(self): r = self.change_password(password=uuid.uuid4().hex, - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertThat(r.result['error']['message'], matchers.Contains('original_password')) def test_changing_password_with_missing_password_fails(self): r = self.change_password(original_password=self.user_ref['password'], - expected_status=400) + expected_status=http_client.BAD_REQUEST) self.assertThat(r.result['error']['message'], matchers.Contains('password')) def test_changing_password_with_incorrect_password_fails(self): self.change_password(password=uuid.uuid4().hex, original_password=uuid.uuid4().hex, - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_changing_password_with_disabled_user_fails(self): # disable the user account @@ -614,7 +628,7 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): self.change_password(password=uuid.uuid4().hex, original_password=self.user_ref['password'], - expected_status=401) + expected_status=http_client.UNAUTHORIZED) def test_changing_password_not_logged(self): # When a user changes their password, the password isn't logged at any @@ -626,7 +640,7 @@ class UserSelfServiceChangingPasswordsTestCase(test_v3.RestfulTestCase): new_password = uuid.uuid4().hex self.change_password(password=new_password, original_password=self.user_ref['password'], - expected_status=204) + expected_status=http_client.NO_CONTENT) self.assertNotIn(self.user_ref['password'], log_fix.output) self.assertNotIn(new_password, log_fix.output) diff --git a/keystone-moon/keystone/tests/unit/test_v3_oauth1.py b/keystone-moon/keystone/tests/unit/test_v3_oauth1.py index 6c063c5e..3a0d481c 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_oauth1.py +++ b/keystone-moon/keystone/tests/unit/test_v3_oauth1.py @@ -18,6 +18,7 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils from pycadf import cadftaxonomy +from six.moves import http_client from six.moves import urllib from keystone.contrib import oauth1 @@ -139,7 +140,7 @@ class ConsumerCRUDTests(OAuth1Tests): consumer = self._create_single_consumer() consumer_id = consumer['id'] resp = self.delete(self.CONSUMER_URL + '/%s' % consumer_id) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) def test_consumer_get(self): consumer = self._create_single_consumer() @@ -182,7 +183,7 @@ class ConsumerCRUDTests(OAuth1Tests): update_ref['secret'] = uuid.uuid4().hex self.patch(self.CONSUMER_URL + '/%s' % original_id, body={'consumer': update_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_consumer_update_bad_id(self): consumer = self._create_single_consumer() @@ -195,7 +196,7 @@ class ConsumerCRUDTests(OAuth1Tests): update_ref['id'] = update_description self.patch(self.CONSUMER_URL + '/%s' % original_id, body={'consumer': update_ref}, - expected_status=400) + expected_status=http_client.BAD_REQUEST) def test_consumer_update_normalize_field(self): # If update a consumer with a field with : or - in the name, @@ -236,7 +237,7 @@ class ConsumerCRUDTests(OAuth1Tests): def test_consumer_get_bad_id(self): self.get(self.CONSUMER_URL + '/%(consumer_id)s' % {'consumer_id': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) class OAuthFlowTests(OAuth1Tests): @@ -261,7 +262,7 @@ class OAuthFlowTests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.assertTrue(all(i in core.VERIFIER_CHARS for i in self.verifier)) self.assertEqual(8, len(self.verifier)) @@ -291,7 +292,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_no_access_tokens(self): resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -316,7 +317,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens/%(key)s' % {'user_id': self.user_id, 'key': uuid.uuid4().hex}, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_list_all_roles_in_access_token(self): self.test_oauth_flow() @@ -341,7 +342,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): url = ('/users/%(id)s/OS-OAUTH1/access_tokens/%(key)s/roles/%(role)s' % {'id': self.user_id, 'key': self.access_token.key, 'role': uuid.uuid4().hex}) - self.get(url, expected_status=404) + self.get(url, expected_status=http_client.NOT_FOUND) def test_list_and_delete_access_tokens(self): self.test_oauth_flow() @@ -356,7 +357,7 @@ class AccessTokenCRUDTests(OAuthFlowTests): resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # List access_token should be 0 resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -399,13 +400,13 @@ class AuthTokenTests(OAuthFlowTests): resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # Check Keystone Token no longer exists headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.get('/auth/tokens', headers=headers, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_deleting_consumer_also_deletes_tokens(self): self.test_oauth_flow() @@ -414,7 +415,7 @@ class AuthTokenTests(OAuthFlowTests): consumer_id = self.consumer['key'] resp = self.delete('/OS-OAUTH1/consumers/%(consumer_id)s' % {'consumer_id': consumer_id}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # List access_token should be 0 resp = self.get('/users/%(user_id)s/OS-OAUTH1/access_tokens' @@ -426,7 +427,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.head('/auth/tokens', headers=headers, - expected_status=404) + expected_status=http_client.NOT_FOUND) def test_change_user_password_also_deletes_tokens(self): self.test_oauth_flow() @@ -445,7 +446,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.admin_request(path='/auth/tokens', headers=headers, - method='GET', expected_status=404) + method='GET', expected_status=http_client.NOT_FOUND) def test_deleting_project_also_invalidates_tokens(self): self.test_oauth_flow() @@ -462,7 +463,7 @@ class AuthTokenTests(OAuthFlowTests): headers = {'X-Subject-Token': self.keystone_token_id, 'X-Auth-Token': self.keystone_token_id} self.admin_request(path='/auth/tokens', headers=headers, - method='GET', expected_status=404) + method='GET', expected_status=http_client.NOT_FOUND) def test_token_chaining_is_not_allowed(self): self.test_oauth_flow() @@ -477,7 +478,7 @@ class AuthTokenTests(OAuthFlowTests): body=auth_data, token=self.keystone_token_id, method='POST', - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_delete_keystone_tokens_by_consumer_id(self): self.test_oauth_flow() @@ -545,14 +546,14 @@ class AuthTokenTests(OAuthFlowTests): self.post('/OS-TRUST/trusts', body={'trust': ref}, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_oauth_token_cannot_authorize_request_token(self): self.test_oauth_flow() url = self._approve_request_token_url() body = {'roles': [{'id': self.role_id}]} self.put(url, body=body, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_oauth_token_cannot_list_request_tokens(self): self._set_policy({"identity:list_access_tokens": [], @@ -561,7 +562,7 @@ class AuthTokenTests(OAuthFlowTests): self.test_oauth_flow() url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id self.get(url, token=self.keystone_token_id, - expected_status=403) + expected_status=http_client.FORBIDDEN) def _set_policy(self, new_policy): self.tempfile = self.useFixture(temporaryfile.SecureTempFile()) @@ -575,14 +576,16 @@ class AuthTokenTests(OAuthFlowTests): trust_token = self._create_trust_get_token() url = self._approve_request_token_url() body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, token=trust_token, expected_status=403) + self.put(url, body=body, token=trust_token, + expected_status=http_client.FORBIDDEN) def test_trust_token_cannot_list_request_tokens(self): self._set_policy({"identity:list_access_tokens": [], "identity:create_trust": []}) trust_token = self._create_trust_get_token() url = '/users/%s/OS-OAUTH1/access_tokens' % self.user_id - self.get(url, token=trust_token, expected_status=403) + self.get(url, token=trust_token, + expected_status=http_client.FORBIDDEN) class MaliciousOAuth1Tests(OAuth1Tests): @@ -592,7 +595,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer_id = consumer['id'] consumer = {'key': consumer_id, 'secret': uuid.uuid4().hex} url, headers = self._create_request_token(consumer, self.project_id) - self.post(url, headers=headers, expected_status=401) + self.post(url, headers=headers, + expected_status=http_client.UNAUTHORIZED) def test_bad_request_token_key(self): consumer = self._create_single_consumer() @@ -605,7 +609,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): response_content_type='application/x-www-urlformencoded') url = self._authorize_request_token(uuid.uuid4().hex) body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, expected_status=404) + self.put(url, body=body, expected_status=http_client.NOT_FOUND) def test_bad_consumer_id(self): consumer = self._create_single_consumer() @@ -613,7 +617,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer_secret = consumer['secret'] consumer = {'key': consumer_id, 'secret': consumer_secret} url, headers = self._create_request_token(consumer, self.project_id) - self.post(url, headers=headers, expected_status=404) + self.post(url, headers=headers, expected_status=http_client.NOT_FOUND) def test_bad_requested_project_id(self): consumer = self._create_single_consumer() @@ -622,7 +626,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): consumer = {'key': consumer_id, 'secret': consumer_secret} project_id = uuid.uuid4().hex url, headers = self._create_request_token(consumer, project_id) - self.post(url, headers=headers, expected_status=404) + self.post(url, headers=headers, expected_status=http_client.NOT_FOUND) def test_bad_verifier(self): consumer = self._create_single_consumer() @@ -641,13 +645,14 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) verifier = resp.result['token']['oauth_verifier'] self.assertIsNotNone(verifier) request_token.set_verifier(uuid.uuid4().hex) url, headers = self._create_access_token(consumer, request_token) - self.post(url, headers=headers, expected_status=401) + self.post(url, headers=headers, + expected_status=http_client.UNAUTHORIZED) def test_bad_authorizing_roles(self): consumer = self._create_single_consumer() @@ -667,7 +672,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} self.admin_request(path=url, method='PUT', - body=body, expected_status=404) + body=body, expected_status=http_client.NOT_FOUND) def test_expired_authorizing_request_token(self): self.config_fixture.config(group='oauth1', request_token_duration=-1) @@ -691,7 +696,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - self.put(url, body=body, expected_status=401) + self.put(url, body=body, expected_status=http_client.UNAUTHORIZED) def test_expired_creating_keystone_token(self): self.config_fixture.config(group='oauth1', access_token_duration=-1) @@ -714,7 +719,7 @@ class MaliciousOAuth1Tests(OAuth1Tests): url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.request_token.set_verifier(self.verifier) @@ -731,7 +736,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): url, headers, body = self._get_oauth_token(self.consumer, self.access_token) - self.post(url, headers=headers, body=body, expected_status=401) + self.post(url, headers=headers, body=body, + expected_status=http_client.UNAUTHORIZED) def test_missing_oauth_headers(self): endpoint = '/OS-OAUTH1/request_token' @@ -747,7 +753,8 @@ class MaliciousOAuth1Tests(OAuth1Tests): # NOTE(stevemar): To simulate this error, we remove the Authorization # header from the post request. del headers['Authorization'] - self.post(endpoint, headers=headers, expected_status=500) + self.post(endpoint, headers=headers, + expected_status=http_client.INTERNAL_SERVER_ERROR) class OAuthNotificationTests(OAuth1Tests, @@ -823,7 +830,7 @@ class OAuthNotificationTests(OAuth1Tests, url = self._authorize_request_token(request_key) body = {'roles': [{'id': self.role_id}]} - resp = self.put(url, body=body, expected_status=200) + resp = self.put(url, body=body, expected_status=http_client.OK) self.verifier = resp.result['token']['oauth_verifier'] self.assertTrue(all(i in core.VERIFIER_CHARS for i in self.verifier)) self.assertEqual(8, len(self.verifier)) @@ -852,7 +859,7 @@ class OAuthNotificationTests(OAuth1Tests, resp = self.delete('/users/%(user)s/OS-OAUTH1/access_tokens/%(auth)s' % {'user': self.user_id, 'auth': self.access_token.key}) - self.assertResponseStatus(resp, 204) + self.assertResponseStatus(resp, http_client.NO_CONTENT) # Test to ensure the delete access token notification is sent self._assert_notify_sent(access_key, diff --git a/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py b/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py index 48226cd4..86ced724 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py +++ b/keystone-moon/keystone/tests/unit/test_v3_os_revoke.py @@ -15,6 +15,7 @@ import uuid from oslo_utils import timeutils import six +from six.moves import http_client from testtools import matchers from keystone.common import utils @@ -112,7 +113,8 @@ class OSRevokeTests(test_v3.RestfulTestCase, test_v3.JsonHomeTestMixin): self.assertReportedEventMatchesRecorded(events[0], sample, before_time) def test_list_since_invalid(self): - self.get('/OS-REVOKE/events?since=blah', expected_status=400) + self.get('/OS-REVOKE/events?since=blah', + expected_status=http_client.BAD_REQUEST) def test_list_since_valid(self): resp = self.get('/OS-REVOKE/events?since=2013-02-27T18:30:59.999999Z') diff --git a/keystone-moon/keystone/tests/unit/test_v3_protection.py b/keystone-moon/keystone/tests/unit/test_v3_protection.py index 458c61de..296e1d4b 100644 --- a/keystone-moon/keystone/tests/unit/test_v3_protection.py +++ b/keystone-moon/keystone/tests/unit/test_v3_protection.py @@ -17,10 +17,11 @@ import uuid from oslo_config import cfg from oslo_serialization import jsonutils +from six.moves import http_client from keystone import exception from keystone.policy.backends import rules -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit.ksfixtures import temporaryfile from keystone.tests.unit import test_v3 @@ -428,7 +429,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): user2_token = self.get_requested_token(user2_auth) self.get('/auth/tokens', token=user1_token, - headers={'X-Subject-Token': user2_token}, expected_status=403) + headers={'X-Subject-Token': user2_token}, + expected_status=http_client.FORBIDDEN) def test_admin_validate_user_token(self): # An admin can validate a user's token. @@ -459,7 +461,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): token = self.get_requested_token(auth) self.head('/auth/tokens', token=token, - headers={'X-Subject-Token': token}, expected_status=200) + headers={'X-Subject-Token': token}, + expected_status=http_client.OK) def test_user_check_user_token(self): # A user can check one of their own tokens. @@ -472,7 +475,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): token2 = self.get_requested_token(auth) self.head('/auth/tokens', token=token1, - headers={'X-Subject-Token': token2}, expected_status=200) + headers={'X-Subject-Token': token2}, + expected_status=http_client.OK) def test_user_check_other_user_token_rejected(self): # A user cannot check another user's token. @@ -490,7 +494,7 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): self.head('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_check_user_token(self): # An admin can check a user's token. @@ -508,7 +512,8 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): user_token = self.get_requested_token(user_auth) self.head('/auth/tokens', token=admin_token, - headers={'X-Subject-Token': user_token}, expected_status=200) + headers={'X-Subject-Token': user_token}, + expected_status=http_client.OK) def test_user_revoke_same_token(self): # Given a non-admin user token, the token can be used to revoke @@ -552,7 +557,7 @@ class IdentityTestPolicySample(test_v3.RestfulTestCase): self.delete('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_revoke_user_token(self): # An admin can revoke a user's token. @@ -607,7 +612,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, rules.reset() self.config_fixture.config( group='oslo_policy', - policy_file=tests.dirs.etc('policy.v3cloudsample.json')) + policy_file=unit.dirs.etc('policy.v3cloudsample.json')) def load_sample_data(self): # Start by creating a couple of domains @@ -681,7 +686,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, # Return the expected return codes for APIs with and without data # with any specified status overriding the normal values if expected_status is None: - return (200, 201, 204) + return (http_client.OK, http_client.CREATED, + http_client.NO_CONTENT) else: return (expected_status, expected_status, expected_status) @@ -948,7 +954,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( domain_id=self.domainB['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_domain_user_list_assignments_of_domain_failed(self): self.auth = self.build_authentication_request( @@ -958,7 +965,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( domain_id=self.domainA['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_cloud_admin_list_assignments_of_project(self): self.auth = self.build_authentication_request( @@ -986,7 +994,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.assertRoleAssignmentInListResponse(r, project_admin_entity) self.assertRoleAssignmentInListResponse(r, project_user_entity) - @tests.utils.wip('waiting on bug #1437407') + @unit.utils.wip('waiting on bug #1437407') def test_domain_admin_list_assignments_of_project(self): self.auth = self.build_authentication_request( user_id=self.domain_admin_user['id'], @@ -1021,7 +1029,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, collection_url = self.build_role_assignment_query_url( project_id=self.project['id']) - self.get(collection_url, auth=self.auth, expected_status=403) + self.get(collection_url, auth=self.auth, + expected_status=http_client.FORBIDDEN) def test_cloud_admin(self): self.auth = self.build_authentication_request( @@ -1045,7 +1054,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, password=self.domain_admin_user['password'], domain_id=self.domainA['id']) entity_url = '/domains/%s' % self.domainA['id'] - self.get(entity_url, auth=self.auth, expected_status=200) + self.get(entity_url, auth=self.auth) def test_list_user_credentials(self): self.credential_user = self.new_credential_ref(self.just_a_user['id']) @@ -1145,7 +1154,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, user2_token = self.get_requested_token(user2_auth) self.get('/auth/tokens', token=user1_token, - headers={'X-Subject-Token': user2_token}, expected_status=403) + headers={'X-Subject-Token': user2_token}, + expected_status=http_client.FORBIDDEN) def test_admin_validate_user_token(self): # An admin can validate a user's token. @@ -1176,7 +1186,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, token = self.get_requested_token(auth) self.head('/auth/tokens', token=token, - headers={'X-Subject-Token': token}, expected_status=200) + headers={'X-Subject-Token': token}, + expected_status=http_client.OK) def test_user_check_user_token(self): # A user can check one of their own tokens. @@ -1189,7 +1200,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, token2 = self.get_requested_token(auth) self.head('/auth/tokens', token=token1, - headers={'X-Subject-Token': token2}, expected_status=200) + headers={'X-Subject-Token': token2}, + expected_status=http_client.OK) def test_user_check_other_user_token_rejected(self): # A user cannot check another user's token. @@ -1207,7 +1219,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.head('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_check_user_token(self): # An admin can check a user's token. @@ -1225,7 +1237,8 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, user_token = self.get_requested_token(user_auth) self.head('/auth/tokens', token=admin_token, - headers={'X-Subject-Token': user_token}, expected_status=200) + headers={'X-Subject-Token': user_token}, + expected_status=http_client.OK) def test_user_revoke_same_token(self): # Given a non-admin user token, the token can be used to revoke @@ -1269,7 +1282,7 @@ class IdentityTestv3CloudPolicySample(test_v3.RestfulTestCase, self.delete('/auth/tokens', token=user1_token, headers={'X-Subject-Token': user2_token}, - expected_status=403) + expected_status=http_client.FORBIDDEN) def test_admin_revoke_user_token(self): # An admin can revoke a user's token. diff --git a/keystone-moon/keystone/tests/unit/test_versions.py b/keystone-moon/keystone/tests/unit/test_versions.py index 7f722f94..fc8051b2 100644 --- a/keystone-moon/keystone/tests/unit/test_versions.py +++ b/keystone-moon/keystone/tests/unit/test_versions.py @@ -20,11 +20,13 @@ import random import mock from oslo_config import cfg from oslo_serialization import jsonutils +from six.moves import http_client from testtools import matchers as tt_matchers +import webob from keystone.common import json_home from keystone import controllers -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import utils @@ -170,6 +172,8 @@ BASE_ACCESS_TOKEN = ( # TODO(stevemar): Use BASE_IDP_PROTOCOL when bug 1420125 is resolved. FEDERATED_AUTH_URL = ('/OS-FEDERATION/identity_providers/{identity_provider}' '/protocols/{protocol}/auth') +FEDERATED_IDP_SPECIFIC_WEBSSO = ('/auth/OS-FEDERATION/identity_providers/' + '{idp_id}/protocols/{protocol_id}/websso') V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { json_home.build_v3_resource_relation('auth_tokens'): { @@ -345,13 +349,13 @@ V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { 'href-vars': {'user_id': json_home.Parameters.USER_ID, }}, json_home.build_v3_resource_relation('users'): {'href': '/users'}, _build_federation_rel(resource_name='domains'): { - 'href': '/OS-FEDERATION/domains'}, + 'href': '/auth/domains'}, _build_federation_rel(resource_name='websso'): { 'href-template': '/auth/OS-FEDERATION/websso/{protocol_id}', 'href-vars': { 'protocol_id': PROTOCOL_ID_PARAM_RELATION, }}, _build_federation_rel(resource_name='projects'): { - 'href': '/OS-FEDERATION/projects'}, + 'href': '/auth/projects'}, _build_federation_rel(resource_name='saml2'): { 'href': '/auth/OS-FEDERATION/saml2'}, _build_federation_rel(resource_name='ecp'): { @@ -368,6 +372,11 @@ V3_JSON_HOME_RESOURCES_INHERIT_DISABLED = { { 'href-template': '/OS-FEDERATION/identity_providers/{idp_id}', 'href-vars': {'idp_id': IDP_ID_PARAMETER_RELATION, }}, + _build_federation_rel(resource_name='identity_providers'): { + 'href-template': FEDERATED_IDP_SPECIFIC_WEBSSO, + 'href-vars': { + 'idp_id': IDP_ID_PARAMETER_RELATION, + 'protocol_id': PROTOCOL_ID_PARAM_RELATION, }}, _build_federation_rel(resource_name='service_provider'): { 'href-template': '/OS-FEDERATION/service_providers/{sp_id}', @@ -614,6 +623,36 @@ V3_JSON_HOME_RESOURCES_INHERIT_ENABLED.update( ) +class TestClient(object): + def __init__(self, app=None, token=None): + self.app = app + self.token = token + + def request(self, method, path, headers=None, body=None): + if headers is None: + headers = {} + + if self.token: + headers.setdefault('X-Auth-Token', self.token) + + req = webob.Request.blank(path) + req.method = method + for k, v in headers.items(): + req.headers[k] = v + if body: + req.body = body + return req.get_response(self.app) + + def get(self, path, headers=None): + return self.request('GET', path=path, headers=headers) + + def post(self, path, headers=None, body=None): + return self.request('POST', path=path, headers=headers, body=body) + + def put(self, path, headers=None, body=None): + return self.request('PUT', path=path, headers=headers, body=body) + + class _VersionsEqual(tt_matchers.MatchesListwise): def __init__(self, expected): super(_VersionsEqual, self).__init__([ @@ -632,7 +671,7 @@ class _VersionsEqual(tt_matchers.MatchesListwise): ]) -class VersionTestCase(tests.TestCase): +class VersionTestCase(unit.TestCase): def setUp(self): super(VersionTestCase, self).setUp() self.load_backends() @@ -657,7 +696,7 @@ class VersionTestCase(tests.TestCase): link['href'] = port def test_public_versions(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -674,7 +713,7 @@ class VersionTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_admin_versions(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -694,7 +733,7 @@ class VersionTestCase(tests.TestCase): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -710,9 +749,9 @@ class VersionTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_public_version_v2(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -721,9 +760,9 @@ class VersionTestCase(tests.TestCase): self.assertEqual(expected, data) def test_admin_version_v2(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -734,18 +773,18 @@ class VersionTestCase(tests.TestCase): def test_use_site_url_if_endpoint_unset_v2(self): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], 'http://localhost/v2.0/') self.assertEqual(data, expected) def test_public_version_v3(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -755,9 +794,9 @@ class VersionTestCase(tests.TestCase): @utils.wip('waiting on bug #1381961') def test_admin_version_v3(self): - client = tests.TestClient(self.admin_app) + client = TestClient(self.admin_app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -768,9 +807,9 @@ class VersionTestCase(tests.TestCase): def test_use_site_url_if_endpoint_unset_v3(self): self.config_fixture.config(public_endpoint=None, admin_endpoint=None) for app in (self.public_app, self.admin_app): - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], 'http://localhost/v3/') @@ -778,14 +817,14 @@ class VersionTestCase(tests.TestCase): @mock.patch.object(controllers, '_VERSIONS', ['v3']) def test_v2_disabled(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) # request to /v2.0 should fail resp = client.get('/v2.0/') - self.assertEqual(404, resp.status_int) + self.assertEqual(http_client.NOT_FOUND, resp.status_int) # request to /v3 should pass resp = client.get('/v3/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v3_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -811,14 +850,14 @@ class VersionTestCase(tests.TestCase): @mock.patch.object(controllers, '_VERSIONS', ['v2.0']) def test_v3_disabled(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) # request to /v3 should fail resp = client.get('/v3/') - self.assertEqual(404, resp.status_int) + self.assertEqual(http_client.NOT_FOUND, resp.status_int) # request to /v2.0 should pass resp = client.get('/v2.0/') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) data = jsonutils.loads(resp.body) expected = v2_VERSION_RESPONSE self._paste_in_port(expected['version'], @@ -843,7 +882,7 @@ class VersionTestCase(tests.TestCase): self.assertEqual(v2_only_response, data) def _test_json_home(self, path, exp_json_home_data): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get(path, headers={'Accept': 'application/json-home'}) self.assertThat(resp.status, tt_matchers.Equals('200 OK')) @@ -876,7 +915,7 @@ class VersionTestCase(tests.TestCase): # Accept headers with multiple types and qvalues are handled. def make_request(accept_types=None): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) headers = None if accept_types: headers = {'Accept': accept_types} @@ -926,7 +965,7 @@ class VersionTestCase(tests.TestCase): self.assertIsNone(extensions_property) -class VersionSingleAppTestCase(tests.TestCase): +class VersionSingleAppTestCase(unit.TestCase): """Tests running with a single application loaded. These are important because when Keystone is running in Apache httpd @@ -962,7 +1001,7 @@ class VersionSingleAppTestCase(tests.TestCase): else: return CONF.eventlet_server.public_port app = self.loadapp('keystone', app_name) - client = tests.TestClient(app) + client = TestClient(app) resp = client.get('/') self.assertEqual(300, resp.status_int) data = jsonutils.loads(resp.body) @@ -983,7 +1022,7 @@ class VersionSingleAppTestCase(tests.TestCase): self._test_version('admin') -class VersionInheritEnabledTestCase(tests.TestCase): +class VersionInheritEnabledTestCase(unit.TestCase): def setUp(self): super(VersionInheritEnabledTestCase, self).setUp() self.load_backends() @@ -1008,7 +1047,7 @@ class VersionInheritEnabledTestCase(tests.TestCase): # If the request is /v3 and the Accept header is application/json-home # then the server responds with a JSON Home document. - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) resp = client.get('/v3/', headers={'Accept': 'application/json-home'}) self.assertThat(resp.status, tt_matchers.Equals('200 OK')) @@ -1022,7 +1061,7 @@ class VersionInheritEnabledTestCase(tests.TestCase): tt_matchers.Equals(exp_json_home_data)) -class VersionBehindSslTestCase(tests.TestCase): +class VersionBehindSslTestCase(unit.TestCase): def setUp(self): super(VersionBehindSslTestCase, self).setUp() self.load_backends() @@ -1048,7 +1087,7 @@ class VersionBehindSslTestCase(tests.TestCase): return expected def test_versions_without_headers(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) host_name = 'host-%d' % random.randint(10, 30) host_port = random.randint(10000, 30000) host = 'http://%s:%s/' % (host_name, host_port) @@ -1059,7 +1098,7 @@ class VersionBehindSslTestCase(tests.TestCase): self.assertThat(data, _VersionsEqual(expected)) def test_versions_with_header(self): - client = tests.TestClient(self.public_app) + client = TestClient(self.public_app) host_name = 'host-%d' % random.randint(10, 30) host_port = random.randint(10000, 30000) resp = client.get('http://%s:%s/' % (host_name, host_port), diff --git a/keystone-moon/keystone/tests/unit/test_wsgi.py b/keystone-moon/keystone/tests/unit/test_wsgi.py index 62156bd5..2a5cb386 100644 --- a/keystone-moon/keystone/tests/unit/test_wsgi.py +++ b/keystone-moon/keystone/tests/unit/test_wsgi.py @@ -23,13 +23,14 @@ import mock import oslo_i18n from oslo_serialization import jsonutils import six +from six.moves import http_client from testtools import matchers import webob from keystone.common import environment from keystone.common import wsgi from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit class FakeApp(wsgi.Application): @@ -52,7 +53,7 @@ class FakeAttributeCheckerApp(wsgi.Application): self._require_attributes(ref, attr) -class RouterTest(tests.TestCase): +class RouterTest(unit.TestCase): def setUp(self): self.router = wsgi.RoutersBase() super(RouterTest, self).setUp() @@ -68,7 +69,7 @@ class RouterTest(tests.TestCase): status=uuid.uuid4().hex) -class BaseWSGITest(tests.TestCase): +class BaseWSGITest(unit.TestCase): def setUp(self): self.app = FakeApp() super(BaseWSGITest, self).setUp() @@ -111,15 +112,16 @@ class ApplicationTest(BaseWSGITest): resp = wsgi.render_response(body=data) self.assertEqual('200 OK', resp.status) - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) self.assertEqual(body, resp.body) self.assertEqual('X-Auth-Token', resp.headers.get('Vary')) self.assertEqual(str(len(body)), resp.headers.get('Content-Length')) def test_render_response_custom_status(self): - resp = wsgi.render_response(status=(501, 'Not Implemented')) + resp = wsgi.render_response( + status=(http_client.NOT_IMPLEMENTED, 'Not Implemented')) self.assertEqual('501 Not Implemented', resp.status) - self.assertEqual(501, resp.status_int) + self.assertEqual(http_client.NOT_IMPLEMENTED, resp.status_int) def test_successful_require_attribute(self): app = FakeAttributeCheckerApp() @@ -171,14 +173,14 @@ class ApplicationTest(BaseWSGITest): def test_render_response_no_body(self): resp = wsgi.render_response() self.assertEqual('204 No Content', resp.status) - self.assertEqual(204, resp.status_int) + self.assertEqual(http_client.NO_CONTENT, resp.status_int) self.assertEqual(b'', resp.body) self.assertEqual('0', resp.headers.get('Content-Length')) self.assertIsNone(resp.headers.get('Content-Type')) def test_render_response_head_with_body(self): resp = wsgi.render_response({'id': uuid.uuid4().hex}, method='HEAD') - self.assertEqual(200, resp.status_int) + self.assertEqual(http_client.OK, resp.status_int) self.assertEqual(b'', resp.body) self.assertNotEqual(resp.headers.get('Content-Length'), '0') self.assertEqual('application/json', resp.headers.get('Content-Type')) @@ -195,14 +197,14 @@ class ApplicationTest(BaseWSGITest): def test_render_exception(self): e = exception.Unauthorized(message=u'\u7f51\u7edc') resp = wsgi.render_exception(e) - self.assertEqual(401, resp.status_int) + self.assertEqual(http_client.UNAUTHORIZED, resp.status_int) def test_render_exception_host(self): e = exception.Unauthorized(message=u'\u7f51\u7edc') context = {'host_url': 'http://%s:5000' % uuid.uuid4().hex} resp = wsgi.render_exception(e, context=context) - self.assertEqual(401, resp.status_int) + self.assertEqual(http_client.UNAUTHORIZED, resp.status_int) def test_improperly_encoded_params(self): class FakeApp(wsgi.Application): @@ -311,7 +313,7 @@ class MiddlewareTest(BaseWSGITest): self.assertEqual("test", app.kwargs["testkey"]) -class LocalizedResponseTest(tests.TestCase): +class LocalizedResponseTest(unit.TestCase): def test_request_match_default(self): # The default language if no Accept-Language is provided is None req = webob.Request.blank('/') @@ -409,7 +411,7 @@ class LocalizedResponseTest(tests.TestCase): self.assertThat(xlation_mock.called, matchers.Equals(True)) -class ServerTest(tests.TestCase): +class ServerTest(unit.TestCase): def setUp(self): super(ServerTest, self).setUp() diff --git a/keystone-moon/keystone/tests/unit/tests/test_core.py b/keystone-moon/keystone/tests/unit/tests/test_core.py index 2de51c52..50f1309e 100644 --- a/keystone-moon/keystone/tests/unit/tests/test_core.py +++ b/keystone-moon/keystone/tests/unit/tests/test_core.py @@ -19,28 +19,28 @@ from oslo_log import log from sqlalchemy import exc from testtools import matchers -from keystone.tests import unit as tests +from keystone.tests import unit LOG = log.getLogger(__name__) -class BaseTestTestCase(tests.BaseTestCase): +class BaseTestTestCase(unit.BaseTestCase): def test_unexpected_exit(self): # if a test calls sys.exit it raises rather than exiting. self.assertThat(lambda: sys.exit(), - matchers.raises(tests.UnexpectedExit)) + matchers.raises(unit.UnexpectedExit)) -class TestTestCase(tests.TestCase): +class TestTestCase(unit.TestCase): def test_bad_log(self): # If the arguments are invalid for the string in a log it raises an # exception during testing. self.assertThat( lambda: LOG.warn('String %(p1)s %(p2)s', {'p1': 'something'}), - matchers.raises(tests.BadLog)) + matchers.raises(KeyError)) def test_sa_warning(self): self.assertThat( diff --git a/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py b/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py index 4101369c..5f74b430 100644 --- a/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_fernet_provider.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import base64 import datetime import hashlib import os @@ -20,7 +21,7 @@ from oslo_utils import timeutils from keystone.common import config from keystone.common import utils from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.token import provider from keystone.token.providers import fernet @@ -31,7 +32,7 @@ from keystone.token.providers.fernet import utils as fernet_utils CONF = config.CONF -class TestFernetTokenProvider(tests.TestCase): +class TestFernetTokenProvider(unit.TestCase): def setUp(self): super(TestFernetTokenProvider, self).setUp() self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) @@ -56,7 +57,24 @@ class TestFernetTokenProvider(tests.TestCase): uuid.uuid4().hex) -class TestPayloads(tests.TestCase): +class TestTokenFormatter(unit.TestCase): + def test_restore_padding(self): + # 'a' will result in '==' padding, 'aa' will result in '=' padding, and + # 'aaa' will result in no padding. + strings_to_test = ['a', 'aa', 'aaa'] + + for string in strings_to_test: + encoded_string = base64.urlsafe_b64encode(string) + encoded_str_without_padding = encoded_string.rstrip('=') + self.assertFalse(encoded_str_without_padding.endswith('=')) + encoded_str_with_padding_restored = ( + token_formatters.TokenFormatter.restore_padding( + encoded_str_without_padding) + ) + self.assertEqual(encoded_string, encoded_str_with_padding_restored) + + +class TestPayloads(unit.TestCase): def test_uuid_hex_to_byte_conversions(self): payload_cls = token_formatters.BasePayload @@ -387,7 +405,7 @@ class TestPayloads(tests.TestCase): self.assertDictEqual(exp_federated_info, federated_info) -class TestFernetKeyRotation(tests.TestCase): +class TestFernetKeyRotation(unit.TestCase): def setUp(self): super(TestFernetKeyRotation, self).setUp() @@ -512,7 +530,7 @@ class TestFernetKeyRotation(tests.TestCase): self.assertEqual(3, keys) -class TestLoadKeys(tests.TestCase): +class TestLoadKeys(unit.TestCase): def test_non_numeric_files(self): self.useFixture(ksfixtures.KeyRepository(self.config_fixture)) evil_file = os.path.join(CONF.fernet_tokens.key_repository, '~1') diff --git a/keystone-moon/keystone/tests/unit/token/test_pki_provider.py b/keystone-moon/keystone/tests/unit/token/test_pki_provider.py index dad31266..b3ad4c2b 100644 --- a/keystone-moon/keystone/tests/unit/token/test_pki_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_pki_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import pki -class TestPkiTokenProvider(tests.TestCase): +class TestPkiTokenProvider(unit.TestCase): def setUp(self): super(TestPkiTokenProvider, self).setUp() self.provider = pki.Provider() diff --git a/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py b/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py index 4a492bc1..1ffe7cfc 100644 --- a/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_pkiz_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import pkiz -class TestPkizTokenProvider(tests.TestCase): +class TestPkizTokenProvider(unit.TestCase): def setUp(self): super(TestPkizTokenProvider, self).setUp() self.provider = pkiz.Provider() diff --git a/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py b/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py index a12a22d4..6114b723 100644 --- a/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py +++ b/keystone-moon/keystone/tests/unit/token/test_token_data_helper.py @@ -16,11 +16,11 @@ import uuid from testtools import matchers from keystone import exception -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import common -class TestTokenDataHelper(tests.TestCase): +class TestTokenDataHelper(unit.TestCase): def setUp(self): super(TestTokenDataHelper, self).setUp() self.load_backends() diff --git a/keystone-moon/keystone/tests/unit/token/test_token_model.py b/keystone-moon/keystone/tests/unit/token/test_token_model.py index 3959d901..f1398491 100644 --- a/keystone-moon/keystone/tests/unit/token/test_token_model.py +++ b/keystone-moon/keystone/tests/unit/token/test_token_model.py @@ -30,7 +30,6 @@ CONF = cfg.CONF class TestKeystoneTokenModel(core.TestCase): def setUp(self): super(TestKeystoneTokenModel, self).setUp() - self.load_backends() self.v2_sample_token = copy.deepcopy( test_token_provider.SAMPLE_V2_TOKEN) self.v3_sample_token = copy.deepcopy( diff --git a/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py b/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py index b49427f0..5c364490 100644 --- a/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py +++ b/keystone-moon/keystone/tests/unit/token/test_uuid_provider.py @@ -10,11 +10,11 @@ # License for the specific language governing permissions and limitations # under the License. -from keystone.tests import unit as tests +from keystone.tests import unit from keystone.token.providers import uuid -class TestUuidTokenProvider(tests.TestCase): +class TestUuidTokenProvider(unit.TestCase): def setUp(self): super(TestUuidTokenProvider, self).setUp() self.provider = uuid.Provider() |