diff --git a/keystone/tests/common/__init__.py b/keystone/tests/common/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/keystone/tests/common/auth.py b/keystone/tests/common/auth.py new file mode 100644 index 0000000000..6e2f394353 --- /dev/null +++ b/keystone/tests/common/auth.py @@ -0,0 +1,91 @@ +# 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. + + +class AuthTestMixin(object): + """To hold auth building helper functions.""" + + def _build_auth_scope(self, project_id=None, project_name=None, + project_domain_id=None, project_domain_name=None, + domain_id=None, domain_name=None, trust_id=None, + unscoped=None): + scope_data = {} + if unscoped: + scope_data['unscoped'] = {} + if project_id or project_name: + scope_data['project'] = {} + if project_id: + scope_data['project']['id'] = project_id + else: + scope_data['project']['name'] = project_name + if project_domain_id or project_domain_name: + project_domain_json = {} + if project_domain_id: + project_domain_json['id'] = project_domain_id + else: + project_domain_json['name'] = project_domain_name + scope_data['project']['domain'] = project_domain_json + if domain_id or domain_name: + scope_data['domain'] = {} + if domain_id: + scope_data['domain']['id'] = domain_id + else: + scope_data['domain']['name'] = domain_name + if trust_id: + scope_data['OS-TRUST:trust'] = {} + scope_data['OS-TRUST:trust']['id'] = trust_id + return scope_data + + def _build_password_auth(self, user_id=None, username=None, + user_domain_id=None, user_domain_name=None, + password=None): + password_data = {'user': {}} + if user_id: + password_data['user']['id'] = user_id + else: + password_data['user']['name'] = username + if user_domain_id or user_domain_name: + password_data['user']['domain'] = {} + if user_domain_id: + password_data['user']['domain']['id'] = user_domain_id + else: + password_data['user']['domain']['name'] = user_domain_name + password_data['user']['password'] = password + return password_data + + def _build_token_auth(self, token): + return {'id': token} + + def build_authentication_request(self, token=None, user_id=None, + username=None, user_domain_id=None, + user_domain_name=None, password=None, + kerberos=False, **kwargs): + """Build auth dictionary. + + It will create an auth dictionary based on all the arguments + that it receives. + """ + auth_data = {} + auth_data['identity'] = {'methods': []} + if kerberos: + auth_data['identity']['methods'].append('kerberos') + auth_data['identity']['kerberos'] = {} + if token: + auth_data['identity']['methods'].append('token') + auth_data['identity']['token'] = self._build_token_auth(token) + if user_id or username: + auth_data['identity']['methods'].append('password') + auth_data['identity']['password'] = self._build_password_auth( + user_id, username, user_domain_id, user_domain_name, password) + if kwargs: + auth_data['scope'] = self._build_auth_scope(**kwargs) + return {'auth': auth_data} diff --git a/keystone/tests/functional/core.py b/keystone/tests/functional/core.py new file mode 100644 index 0000000000..e055104b9f --- /dev/null +++ b/keystone/tests/functional/core.py @@ -0,0 +1,85 @@ +# 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 os + +import requests +import testtools + +from keystone.tests.common import auth as common_auth + + +class BaseTestCase(testtools.TestCase, common_auth.AuthTestMixin): + + request_headers = {'content-type': 'application/json'} + + def setUp(self): + self.ADMIN_URL = os.environ.get('KSTEST_ADMIN_URL', + 'http://localhost:35357') + self.PUBLIC_URL = os.environ.get('KSTEST_PUBLIC_URL', + 'http://localhost:5000') + self.admin = { + 'name': os.environ.get('KSTEST_ADMIN_USERNAME', 'admin'), + 'password': os.environ.get('KSTEST_ADMIN_PASSWORD', ''), + 'domain_id': os.environ.get('KSTEST_ADMIN_DOMAIN_ID', 'default') + } + + self.user = { + 'name': os.environ.get('KSTEST_USER_USERNAME', 'demo'), + 'password': os.environ.get('KSTEST_USER_PASSWORD', ''), + 'domain_id': os.environ.get('KSTEST_USER_DOMAIN_ID', 'default') + } + + self.project_id = os.environ.get('KSTEST_PROJECT_ID') + + super(BaseTestCase, self).setUp() + + def _http_headers(self, token=None): + headers = {'content-type': 'application/json'} + if token: + headers['X-Auth-Token'] = token + return headers + + def get_scoped_token_response(self, user): + """Convenience method so that we can test authenticated requests + + :param user: A dictionary with user information like 'username', + 'password', 'domain_id' + :returns: urllib3.Response object + + """ + body = self.build_authentication_request( + username=user['name'], user_domain_name=user['domain_id'], + password=user['password'], project_id=self.project_id) + return requests.post(self.PUBLIC_URL + '/v3/auth/tokens', + headers=self.request_headers, + json=body) + + def get_scoped_token(self, user): + """Convenience method for getting scoped token + + This method doesn't do any token validaton. + + :param user: A dictionary with user information like 'username', + 'password', 'domain_id' + :returns: An OpenStack token for further use + :rtype: str + + """ + r = self.get_scoped_token_response(user) + return r.headers.get('X-Subject-Token') + + def get_scoped_admin_token(self): + return self.get_scoped_token(self.admin) + + def get_scoped_user_token(self): + return self.get_scoped_token(self.user) diff --git a/keystone/tests/functional/shared/test_running.py b/keystone/tests/functional/shared/test_running.py index b9889c97bd..1b46b32d94 100644 --- a/keystone/tests/functional/shared/test_running.py +++ b/keystone/tests/functional/shared/test_running.py @@ -13,42 +13,46 @@ import requests import testtools.matchers +from keystone.tests.functional import core as functests + is_multiple_choices = testtools.matchers.Equals( requests.status_codes.codes.multiple_choices) is_ok = testtools.matchers.Equals(requests.status_codes.codes.ok) +versions = ('v2.0', 'v3') -class TestServerRunning(testtools.TestCase): - versions = ('v2.0', 'v3') - admin_url = 'http://localhost:35357' - public_url = 'http://localhost:5000' + +class TestServerRunning(functests.BaseTestCase): def test_admin_responds_with_multiple_choices(self): - resp = requests.get(self.admin_url) + resp = requests.get(self.ADMIN_URL) self.assertThat(resp.status_code, is_multiple_choices) def test_admin_versions(self): - for version in self.versions: - resp = requests.get(self.admin_url + '/' + version) + for version in versions: + resp = requests.get(self.ADMIN_URL + '/' + version) self.assertThat( resp.status_code, testtools.matchers.Annotate( 'failed for version %s' % version, is_ok)) def test_public_responds_with_multiple_choices(self): - resp = requests.get(self.public_url) + resp = requests.get(self.PUBLIC_URL) self.assertThat(resp.status_code, is_multiple_choices) def test_public_versions(self): - for version in self.versions: - resp = requests.get(self.public_url + '/' + version) + for version in versions: + resp = requests.get(self.PUBLIC_URL + '/' + version) self.assertThat( resp.status_code, testtools.matchers.Annotate( 'failed for version %s' % version, is_ok)) + def test_get_user_token(self): + token = self.get_scoped_user_token() + self.assertIsNotNone(token) -class TestServerRunningOnPath(TestServerRunning): - admin_url = 'http://localhost/identity_admin' - public_url = 'http://localhost/identity' + def test_get_admin_token(self): + token = self.get_scoped_admin_token() + self.assertIsNotNone(token) diff --git a/keystone/tests/unit/test_v3.py b/keystone/tests/unit/test_v3.py index 16850fe308..11f0059446 100644 --- a/keystone/tests/unit/test_v3.py +++ b/keystone/tests/unit/test_v3.py @@ -27,6 +27,7 @@ from keystone.common import cache from keystone import exception from keystone import middleware from keystone.policy.backends import rules +from keystone.tests.common import auth as common_auth from keystone.tests import unit from keystone.tests.unit import rest @@ -118,7 +119,7 @@ class AuthTestMixin(object): class RestfulTestCase(unit.SQLDriverOverrides, rest.RestfulTestCase, - AuthTestMixin): + common_auth.AuthTestMixin): def config_files(self): config_files = super(RestfulTestCase, self).config_files() config_files.append(unit.dirs.tests_conf('backend_sql.conf')) diff --git a/keystone/tests/unit/test_v3_auth.py b/keystone/tests/unit/test_v3_auth.py index bb1311b329..ab9e2d7f56 100644 --- a/keystone/tests/unit/test_v3_auth.py +++ b/keystone/tests/unit/test_v3_auth.py @@ -31,6 +31,7 @@ from keystone import auth from keystone.common import utils from keystone import exception from keystone.policy.backends import rules +from keystone.tests.common import auth as common_auth from keystone.tests import unit from keystone.tests.unit import ksfixtures from keystone.tests.unit import test_v3 @@ -38,7 +39,7 @@ from keystone.tests.unit import test_v3 CONF = cfg.CONF -class TestAuthInfo(test_v3.AuthTestMixin, testcase.TestCase): +class TestAuthInfo(common_auth.AuthTestMixin, testcase.TestCase): def setUp(self): super(TestAuthInfo, self).setUp() auth.controllers.load_auth_methods() diff --git a/tox.ini b/tox.ini index 370aa67294..dc9cede444 100644 --- a/tox.ini +++ b/tox.ini @@ -92,12 +92,32 @@ commands = {posargs} [testenv:debug] commands = oslo_debug_helper {posargs} +passenv = + KSTEST_ADMIN_URL + KSTEST_ADMIN_USERNAME + KSTEST_ADMIN_PASSWORD + KSTEST_ADMIN_DOMAIN_ID + KSTEST_PUBLIC_URL + KSTEST_USER_USERNAME + KSTEST_USER_PASSWORD + KSTEST_USER_DOMAIN_ID + KSTEST_PROJECT_ID [testenv:functional] basepython = python3.4 deps = -r{toxinidir}/test-requirements.txt setenv = OS_TEST_PATH=./keystone/tests/functional commands = python setup.py testr --slowest --testr-args='{posargs}' +passenv = + KSTEST_ADMIN_URL + KSTEST_ADMIN_USERNAME + KSTEST_ADMIN_PASSWORD + KSTEST_ADMIN_DOMAIN_ID + KSTEST_PUBLIC_URL + KSTEST_USER_USERNAME + KSTEST_USER_PASSWORD + KSTEST_USER_DOMAIN_ID + KSTEST_PROJECT_ID [flake8] filename= *.py,keystone-all,keystone-manage