diff options
Diffstat (limited to 'keystoneclient')
41 files changed, 646 insertions, 363 deletions
diff --git a/keystoneclient/adapter.py b/keystoneclient/adapter.py index 3cb4dc4..e14ce7d 100644 --- a/keystoneclient/adapter.py +++ b/keystoneclient/adapter.py @@ -116,6 +116,9 @@ class Adapter(object): :returns: An endpoint if available or None. :rtype: string """ + if self.endpoint_override: + return self.endpoint_override + self._set_endpoint_filter_kwargs(kwargs) return self.session.get_endpoint(auth or self.auth, **kwargs) @@ -123,6 +126,40 @@ class Adapter(object): """Invalidate an authentication plugin.""" return self.session.invalidate(auth or self.auth) + def get_user_id(self, auth=None): + """Return the authenticated user_id as provided by the auth plugin. + + :param auth: The auth plugin to use for token. Overrides the plugin + on the session. (optional) + :type auth: keystoneclient.auth.base.BaseAuthPlugin + + :raises keystoneclient.exceptions.AuthorizationFailure: + if a new token fetch fails. + :raises keystoneclient.exceptions.MissingAuthPlugin: + if a plugin is not available. + + :returns: Current `user_id` or None if not supported by plugin. + :rtype: string + """ + return self.session.get_user_id(auth or self.auth) + + def get_project_id(self, auth=None): + """Return the authenticated project_id as provided by the auth plugin. + + :param auth: The auth plugin to use for token. Overrides the plugin + on the session. (optional) + :type auth: keystoneclient.auth.base.BaseAuthPlugin + + :raises keystoneclient.exceptions.AuthorizationFailure: + if a new token fetch fails. + :raises keystoneclient.exceptions.MissingAuthPlugin: + if a plugin is not available. + + :returns: Current `project_id` or None if not supported by plugin. + :rtype: string + """ + return self.session.get_project_id(auth or self.auth) + def get(self, url, **kwargs): return self.request(url, 'GET', **kwargs) diff --git a/keystoneclient/auth/base.py b/keystoneclient/auth/base.py index 1f4ce29..9da90b7 100644 --- a/keystoneclient/auth/base.py +++ b/keystoneclient/auth/base.py @@ -34,6 +34,7 @@ def get_plugin_class(name): :param str name: The name of the object to get. :returns: An auth plugin class. + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be created. @@ -67,6 +68,8 @@ class BaseAuthPlugin(object): Returning None will indicate that no token was able to be retrieved. :param session: A session object so the plugin can make HTTP calls. + :type session: keystoneclient.session.Session + :return: A token to use. :rtype: string """ @@ -84,8 +87,8 @@ class BaseAuthPlugin(object): - ``interface``: what visibility the endpoint should have. - ``region_name``: the region the endpoint exists in. - :param Session session: The session object that the auth_plugin - belongs to. + :param session: The session object that the auth_plugin belongs to. + :type session: keystoneclient.session.Session :returns: The base URL that will be used to talk to the required service or None if not available. @@ -108,6 +111,36 @@ class BaseAuthPlugin(object): """ return False + def get_user_id(self, session, **kwargs): + """Return a unique user identifier of the plugin. + + Wherever possible the user id should be inferred from the token however + there are certain URLs and other places that require access to the + currently authenticated user id. + + :param session: A session object so the plugin can make HTTP calls. + :type session: keystoneclient.session.Session + + :returns: A user identifier or None if one is not available. + :rtype: str + """ + return None + + def get_project_id(self, session, **kwargs): + """Return the project id that we are authenticated to. + + Wherever possible the project id should be inferred from the token + however there are certain URLs and other places that require access to + the currently authenticated project id. + + :param session: A session object so the plugin can make HTTP calls. + :type session: keystoneclient.session.Session + + :returns: A project identifier or None if one is not available. + :rtype: str + """ + return None + @classmethod def get_options(cls): """Return the list of parameters associated with the auth plugin. @@ -137,8 +170,8 @@ class BaseAuthPlugin(object): Given a plugin class convert it's options into argparse arguments and add them to a parser. - :param AuthPlugin plugin: an auth plugin class. - :param argparse.ArgumentParser: the parser to attach argparse options. + :param parser: the parser to attach argparse options. + :type parser: argparse.ArgumentParser """ # NOTE(jamielennox): ideally oslo.config would be smart enough to @@ -155,14 +188,12 @@ class BaseAuthPlugin(object): args.append('--os-%s' % o.name) envs.append('OS_%s' % o.name.replace('-', '_').upper()) - default = opt.default - if default is None: - # select the first ENV that is not false-y or return None - env_vars = (os.environ.get(e) for e in envs) - default = six.next(six.moves.filter(None, env_vars), None) + # select the first ENV that is not false-y or return None + env_vars = (os.environ.get(e) for e in envs) + default = six.next(six.moves.filter(None, env_vars), None) parser.add_argument(*args, - default=default, + default=default or opt.default, metavar=opt.metavar, help=opt.help, dest='os_%s' % opt.dest) @@ -173,10 +204,11 @@ class BaseAuthPlugin(object): Convert the results of a parse into the specified plugin. - :param AuthPlugin plugin: an auth plugin class. - :param Namespace namespace: The result from CLI parsing. + :param namespace: The result from CLI parsing. + :type namespace: argparse.Namespace :returns: An auth plugin, or None if a name is not provided. + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` """ for opt in cls.get_options(): val = getattr(namespace, 'os_%s' % opt.dest) @@ -190,7 +222,8 @@ class BaseAuthPlugin(object): def register_conf_options(cls, conf, group): """Register the oslo.config options that are needed for a plugin. - :param conf: An oslo.config conf object. + :param conf: A config object. + :type conf: oslo.config.cfg.ConfigOpts :param string group: The group name that options should be read from. """ plugin_opts = cls.get_options() @@ -202,11 +235,12 @@ class BaseAuthPlugin(object): Convert the options already registered into a real plugin. - :param conf: An oslo.config conf object. + :param conf: A config object. + :type conf: oslo.config.cfg.ConfigOpts :param string group: The group name that options should be read from. :returns: An authentication Plugin. - :rtype: plugin: + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` """ plugin_opts = cls.get_options() diff --git a/keystoneclient/auth/cli.py b/keystoneclient/auth/cli.py index ce4f11f..40a81c1 100644 --- a/keystoneclient/auth/cli.py +++ b/keystoneclient/auth/cli.py @@ -30,6 +30,7 @@ def register_argparse_arguments(parser, argv, default=None): if one isn't specified by the CLI. default: None. :returns: The plugin class that will be loaded or None if not provided. + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be created. @@ -68,6 +69,7 @@ def load_from_argparse_arguments(namespace, **kwargs): :param Namespace namespace: The result from CLI parsing. :returns: An auth plugin, or None if a name is not provided. + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be created. diff --git a/keystoneclient/auth/conf.py b/keystoneclient/auth/conf.py index 0b68184..fdd2aa4 100644 --- a/keystoneclient/auth/conf.py +++ b/keystoneclient/auth/conf.py @@ -60,7 +60,8 @@ def register_conf_options(conf, group): taken. If section is not provided then the auth plugin options will be taken from the same group as provided in the parameters. - :param oslo.config.Cfg conf: config object to register with. + :param conf: config object to register with. + :type conf: oslo.config.cfg.ConfigOpts :param string group: The ini group to register options in. """ conf.register_opt(_AUTH_SECTION_OPT, group=group) @@ -85,11 +86,12 @@ def load_from_conf_options(conf, group, **kwargs): The base options should have been registered with register_conf_options before this function is called. - :param conf: An oslo.config conf object. + :param conf: A conf object. + :type conf: oslo.config.cfg.ConfigOpts :param string group: The group name that options should be read from. :returns: An authentication Plugin or None if a name is not provided - :rtype: plugin + :rtype: :py:class:`keystoneclient.auth.BaseAuthPlugin` :raises keystoneclient.exceptions.NoMatchingPlugin: if a plugin cannot be created. diff --git a/keystoneclient/auth/identity/base.py b/keystoneclient/auth/identity/base.py index 94b0712..ad8adb0 100644 --- a/keystoneclient/auth/identity/base.py +++ b/keystoneclient/auth/identity/base.py @@ -74,6 +74,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): when invoked. If you are looking to just retrieve the current auth data then you should use get_access. + :param session: A session object that can be used for communication. + :type session: keystoneclient.session.Session + :raises keystoneclient.exceptions.InvalidResponse: The response returned wasn't appropriate. @@ -89,6 +92,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): If a valid token is not present then a new one will be fetched. + :param session: A session object that can be used for communication. + :type session: keystoneclient.session.Session + :raises keystoneclient.exceptions.HttpError: An error from an invalid HTTP response. @@ -125,6 +131,9 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): If a valid AccessInfo is present then it is returned otherwise a new one will be fetched. + :param session: A session object that can be used for communication. + :type session: keystoneclient.session.Session + :raises keystoneclient.exceptions.HttpError: An error from an invalid HTTP response. @@ -164,6 +173,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): If a valid token is not present then a new one will be fetched using the session and kwargs. + :param session: A session object that can be used for communication. + :type session: keystoneclient.session.Session :param string service_type: The type of service to lookup the endpoint for. This plugin will return None (failure) if service_type is not provided. @@ -236,6 +247,12 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): return url + def get_user_id(self, session, **kwargs): + return self.get_access(session).user_id + + def get_project_id(self, session, **kwargs): + return self.get_access(session).project_id + @utils.positional() def get_discovery(self, session, url, authenticated=None): """Return the discovery object for a URL. @@ -247,7 +264,8 @@ class BaseIdentityPlugin(base.BaseAuthPlugin): This function is expected to be used by subclasses and should not be needed by users. - :param Session session: A session object to discover with. + :param session: A session object to discover with. + :type session: keystoneclient.session.Session :param str url: The url to lookup. :param bool authenticated: Include a token in the discovery call. (optional) Defaults to None (use a token diff --git a/keystoneclient/auth/identity/generic/base.py b/keystoneclient/auth/identity/generic/base.py index 4fa3c9c..7c5a80f 100644 --- a/keystoneclient/auth/identity/generic/base.py +++ b/keystoneclient/auth/identity/generic/base.py @@ -81,7 +81,8 @@ class BaseGenericPlugin(base.BaseIdentityPlugin): params then it should return it. If not return None and then another call will be made with other available URLs. - :param Session session: A session object. + :param session: A session object. + :type session: keystoneclient.session.Session :param tuple version: A tuple of the API version at the URL. :param string url: The base URL for this version. :param string raw_status: The status that was in the discovery field. diff --git a/keystoneclient/auth/identity/v3.py b/keystoneclient/auth/identity/v3.py index 8f723ff..cc3a62c 100644 --- a/keystoneclient/auth/identity/v3.py +++ b/keystoneclient/auth/identity/v3.py @@ -176,7 +176,8 @@ class AuthMethod(object): def get_auth_data(self, session, auth, headers, **kwargs): """Return the authentication section of an auth plugin. - :param Session session: The communication session. + :param session: The communication session. + :type session: keystoneclient.session.Session :param Auth auth: The auth plugin calling the method. :param dict headers: The headers that will be sent with the auth request if a plugin needs to add to them. diff --git a/keystoneclient/client.py b/keystoneclient/client.py index 8b6a6b0..8de2963 100644 --- a/keystoneclient/client.py +++ b/keystoneclient/client.py @@ -28,9 +28,10 @@ def Client(version=None, unstable=False, session=None, **kwargs): at least the specified minor version. For example to specify the 3.1 API use (3, 1). :param bool unstable: Accept endpoints not marked as 'stable'. (optional) - :param Session session: A session object to be used for communication. If - one is not provided it will be constructed from the - provided kwargs. (optional) + :param session: A session object to be used for communication. If one is + not provided it will be constructed from the provided + kwargs. (optional) + :type session: keystoneclient.session.Session :param kwargs: Additional arguments are passed through to the client that is being created. :returns: New keystone client object diff --git a/keystoneclient/common/cms.py b/keystoneclient/common/cms.py index d49a0c5..19390f2 100644 --- a/keystoneclient/common/cms.py +++ b/keystoneclient/common/cms.py @@ -23,6 +23,7 @@ import base64 import errno import hashlib import logging +import textwrap import zlib import six @@ -227,20 +228,10 @@ def pkiz_verify(signed_text, signing_cert_file_name, ca_file_name): def token_to_cms(signed_text): copy_of_text = signed_text.replace('-', '/') - formatted = '-----BEGIN CMS-----\n' - line_length = 64 - while len(copy_of_text) > 0: - if (len(copy_of_text) > line_length): - formatted += copy_of_text[:line_length] - copy_of_text = copy_of_text[line_length:] - else: - formatted += copy_of_text - copy_of_text = '' - formatted += '\n' - - formatted += '-----END CMS-----\n' - - return formatted + lines = ['-----BEGIN CMS-----'] + lines += textwrap.wrap(copy_of_text, 64) + lines.append('-----END CMS-----\n') + return '\n'.join(lines) def verify_token(token, signing_cert_file_name, ca_file_name): diff --git a/keystoneclient/httpclient.py b/keystoneclient/httpclient.py index 458745f..84b314e 100644 --- a/keystoneclient/httpclient.py +++ b/keystoneclient/httpclient.py @@ -362,6 +362,12 @@ class HTTPClient(baseclient.Client, base.BaseAuthPlugin): else: return self.management_url + def get_user_id(self, session, **kwargs): + return self.auth_ref.user_id + + def get_project_id(self, session, **kwargs): + return self.auth_ref.project_id + @auth_token.setter def auth_token(self, value): """Override the auth_token. diff --git a/keystoneclient/session.py b/keystoneclient/session.py index d68e209..0c3edbb 100644 --- a/keystoneclient/session.py +++ b/keystoneclient/session.py @@ -53,6 +53,21 @@ def request(url, method='GET', **kwargs): return Session().request(url, method=method, **kwargs) +def remove_service_catalog(body): + try: + data = jsonutils.loads(body) + except ValueError: + return body + try: + if 'catalog' in data['token']: + data['token']['catalog'] = '<removed>' + return jsonutils.dumps(data) + else: + return body + except KeyError: + return body + + class Session(object): """Maintains client communication state and common functionality. @@ -182,7 +197,7 @@ class Session(object): if not headers: headers = response.headers if not text: - text = response.text + text = remove_service_catalog(response.text) if json: text = jsonutils.dumps(json) @@ -534,6 +549,16 @@ class Session(object): return cls(verify=verify, cert=cert, **kwargs) + def _auth_required(self, auth, msg): + if not auth: + auth = self.auth + + if not auth: + msg_fmt = _('An auth plugin is required to %s') + raise exceptions.MissingAuthPlugin(msg_fmt % msg) + + return auth + def get_token(self, auth=None): """Return a token as provided by the auth plugin. @@ -549,11 +574,7 @@ class Session(object): :returns: A valid token. :rtype: string """ - if not auth: - auth = self.auth - - if not auth: - raise exceptions.MissingAuthPlugin(_("Token Required")) + auth = self._auth_required(auth, 'fetch a token') try: return auth.get_token(self) @@ -574,14 +595,7 @@ class Session(object): :returns: An endpoint if available or None. :rtype: string """ - if not auth: - auth = self.auth - - if not auth: - raise exceptions.MissingAuthPlugin( - _('An auth plugin is required to determine the endpoint ' - 'URL.')) - + auth = self._auth_required(auth, 'determine endpoint URL') return auth.get_endpoint(self, **kwargs) def invalidate(self, auth=None): @@ -592,14 +606,42 @@ class Session(object): :type auth: :py:class:`keystoneclient.auth.base.BaseAuthPlugin` """ - if not auth: - auth = self.auth + auth = self._auth_required(auth, 'validate') + return auth.invalidate() - if not auth: - msg = _('Auth plugin not available to invalidate') - raise exceptions.MissingAuthPlugin(msg) + def get_user_id(self, auth=None): + """Return the authenticated user_id as provided by the auth plugin. - return auth.invalidate() + :param auth: The auth plugin to use for token. Overrides the plugin + on the session. (optional) + :type auth: keystoneclient.auth.base.BaseAuthPlugin + + :raises keystoneclient.exceptions.AuthorizationFailure: + if a new token fetch fails. + :raises keystoneclient.exceptions.MissingAuthPlugin: + if a plugin is not available. + + :returns string: Current user_id or None if not supported by plugin. + """ + auth = self._auth_required(auth, 'get user_id') + return auth.get_user_id(self) + + def get_project_id(self, auth=None): + """Return the authenticated project_id as provided by the auth plugin. + + :param auth: The auth plugin to use for token. Overrides the plugin + on the session. (optional) + :type auth: keystoneclient.auth.base.BaseAuthPlugin + + :raises keystoneclient.exceptions.AuthorizationFailure: + if a new token fetch fails. + :raises keystoneclient.exceptions.MissingAuthPlugin: + if a plugin is not available. + + :returns string: Current project_id or None if not supported by plugin. + """ + auth = self._auth_required(auth, 'get project_id') + return auth.get_project_id(self) @utils.positional.classmethod() def get_conf_options(cls, deprecated_opts=None): diff --git a/keystoneclient/tests/auth/test_cli.py b/keystoneclient/tests/auth/test_cli.py index fc09195..33c2868 100644 --- a/keystoneclient/tests/auth/test_cli.py +++ b/keystoneclient/tests/auth/test_cli.py @@ -13,6 +13,7 @@ import argparse import uuid +import fixtures import mock from oslo.config import cfg @@ -43,6 +44,13 @@ class CliTests(utils.TestCase): super(CliTests, self).setUp() self.p = argparse.ArgumentParser() + def env(self, name, value=None): + if value is not None: + # environment variables are always strings + value = str(value) + + return self.useFixture(fixtures.EnvironmentVariable(name, value)) + def test_creating_with_no_args(self): ret = cli.register_argparse_arguments(self.p, []) self.assertIsNone(ret) @@ -140,6 +148,18 @@ class CliTests(utils.TestCase): self.assertIs(utils.MockPlugin, klass) m.assert_called_once_with(name) + @utils.mock_plugin + def test_env_overrides_default_opt(self, m): + name = uuid.uuid4().hex + val = uuid.uuid4().hex + self.env('OS_A_STR', val) + + klass = cli.register_argparse_arguments(self.p, [], default=name) + opts = self.p.parse_args([]) + a = klass.load_from_argparse_arguments(opts) + + self.assertEqual(val, a['a_str']) + def test_deprecated_cli_options(self): TesterPlugin.register_argparse_arguments(self.p) val = uuid.uuid4().hex diff --git a/keystoneclient/tests/auth/test_identity_common.py b/keystoneclient/tests/auth/test_identity_common.py index 4a0cf57..a7d9be6 100644 --- a/keystoneclient/tests/auth/test_identity_common.py +++ b/keystoneclient/tests/auth/test_identity_common.py @@ -65,6 +65,13 @@ class CommonIdentityTests(object): def stub_auth_data(self, **kwargs): token = self.get_auth_data(**kwargs) + self.user_id = token.user_id + + try: + self.project_id = token.project_id + except AttributeError: + self.project_id = token.tenant_id + self.stub_auth(json=token) @abc.abstractproperty @@ -107,7 +114,7 @@ class CommonIdentityTests(object): # register responses such that if the discovery URL is hit more than # once then the response will be invalid and not point to COMPUTE_ADMIN resps = [{'json': self.TEST_DISCOVERY}, {'status_code': 500}] - self.requests.register_uri('GET', self.TEST_COMPUTE_ADMIN, resps) + self.requests.get(self.TEST_COMPUTE_ADMIN, resps) body = 'SUCCESS' self.stub_url('GET', ['path'], text=body) @@ -132,7 +139,7 @@ class CommonIdentityTests(object): # register responses such that if the discovery URL is hit more than # once then the response will be invalid and not point to COMPUTE_ADMIN resps = [{'json': self.TEST_DISCOVERY}, {'status_code': 500}] - self.requests.register_uri('GET', self.TEST_COMPUTE_ADMIN, resps) + self.requests.get(self.TEST_COMPUTE_ADMIN, resps) body = 'SUCCESS' self.stub_url('GET', ['path'], text=body) @@ -221,6 +228,13 @@ class CommonIdentityTests(object): self.assertIsNone(a.auth_ref) self.assertFalse(a.invalidate()) + def test_get_auth_properties(self): + a = self.create_auth_plugin() + s = session.Session() + + self.assertEqual(self.user_id, a.get_user_id(s)) + self.assertEqual(self.project_id, a.get_project_id(s)) + class V3(CommonIdentityTests, utils.TestCase): diff --git a/keystoneclient/tests/auth/test_identity_v3.py b/keystoneclient/tests/auth/test_identity_v3.py index bce4fa7..d869dba 100644 --- a/keystoneclient/tests/auth/test_identity_v3.py +++ b/keystoneclient/tests/auth/test_identity_v3.py @@ -431,8 +431,7 @@ class V3IdentityPlugin(utils.TestCase): {'status_code': 200, 'json': self.TEST_RESPONSE_DICT, 'headers': {'X-Subject-Token': 'token2'}}] - self.requests.register_uri('POST', '%s/auth/tokens' % self.TEST_URL, - auth_responses) + self.requests.post('%s/auth/tokens' % self.TEST_URL, auth_responses) a = v3.Password(self.TEST_URL, username=self.TEST_USER, password=self.TEST_PASS) diff --git a/keystoneclient/tests/auth/test_token_endpoint.py b/keystoneclient/tests/auth/test_token_endpoint.py index a9028e3..fa64b39 100644 --- a/keystoneclient/tests/auth/test_token_endpoint.py +++ b/keystoneclient/tests/auth/test_token_endpoint.py @@ -23,7 +23,7 @@ class TokenEndpointTest(utils.TestCase): TEST_URL = 'http://server/prefix' def test_basic_case(self): - self.requests.register_uri('GET', self.TEST_URL, text='body') + self.requests.get(self.TEST_URL, text='body') a = token_endpoint.Token(self.TEST_URL, self.TEST_TOKEN) s = session.Session(auth=a) @@ -53,3 +53,11 @@ class TokenEndpointTest(utils.TestCase): self.assertIn('token', opt_names) self.assertIn('endpoint', opt_names) + + def test_token_endpoint_user_id(self): + a = token_endpoint.Token(self.TEST_URL, self.TEST_TOKEN) + s = session.Session() + + # we can't know this information about this sort of plugin + self.assertIsNone(a.get_user_id(s)) + self.assertIsNone(a.get_project_id(s)) diff --git a/keystoneclient/tests/auth/utils.py b/keystoneclient/tests/auth/utils.py index c3dae8f..cfca379 100644 --- a/keystoneclient/tests/auth/utils.py +++ b/keystoneclient/tests/auth/utils.py @@ -30,6 +30,8 @@ class MockPlugin(base.BaseAuthPlugin): INT_DESC = 'test int' FLOAT_DESC = 'test float' BOOL_DESC = 'test bool' + STR_DESC = 'test str' + STR_DEFAULT = uuid.uuid4().hex def __init__(self, **kwargs): self._data = kwargs @@ -49,6 +51,7 @@ class MockPlugin(base.BaseAuthPlugin): cfg.IntOpt('a-int', default='3', help=cls.INT_DESC), cfg.BoolOpt('a-bool', help=cls.BOOL_DESC), cfg.FloatOpt('a-float', help=cls.FLOAT_DESC), + cfg.StrOpt('a-str', help=cls.STR_DESC, default=cls.STR_DEFAULT), ] diff --git a/keystoneclient/tests/generic/test_client.py b/keystoneclient/tests/generic/test_client.py index 0f25f41..e5f8736 100644 --- a/keystoneclient/tests/generic/test_client.py +++ b/keystoneclient/tests/generic/test_client.py @@ -56,8 +56,7 @@ EXTENSION_LIST = _create_extension_list([EXTENSION_FOO, EXTENSION_BAR]) class ClientDiscoveryTests(utils.TestCase): def test_discover_extensions_v2(self): - self.requests.register_uri('GET', "%s/extensions" % V2_URL, - text=EXTENSION_LIST) + self.requests.get("%s/extensions" % V2_URL, text=EXTENSION_LIST) extensions = client.Client().discover_extensions(url=V2_URL) self.assertIn(EXTENSION_ALIAS_FOO, extensions) self.assertEqual(extensions[EXTENSION_ALIAS_FOO], EXTENSION_NAME_FOO) diff --git a/keystoneclient/tests/test_auth_token_middleware.py b/keystoneclient/tests/test_auth_token_middleware.py index 4e70b73..d36fc48 100644 --- a/keystoneclient/tests/test_auth_token_middleware.py +++ b/keystoneclient/tests/test_auth_token_middleware.py @@ -28,6 +28,7 @@ import mock from oslo.serialization import jsonutils from oslo.utils import timeutils from requests_mock.contrib import fixture as mock_fixture +import six from six.moves.urllib import parse as urlparse import testresources import testtools @@ -133,6 +134,22 @@ class TimezoneFixture(fixtures.Fixture): time.tzset() +class TimeFixture(fixtures.Fixture): + + def __init__(self, new_time, normalize=True): + super(TimeFixture, self).__init__() + if isinstance(new_time, six.string_types): + new_time = timeutils.parse_isotime(new_time) + if normalize: + new_time = timeutils.normalize_time(new_time) + self.new_time = new_time + + def setUp(self): + super(TimeFixture, self).setUp() + timeutils.set_time_override(self.new_time) + self.addCleanup(timeutils.clear_time_override) + + class FakeApp(object): """This represents a WSGI app protected by the auth_token middleware.""" @@ -258,13 +275,11 @@ class MultiStepAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, # Get a token, then try to retrieve revocation list and get a 401. # Get a new token, try to retrieve revocation list and return 200. - self.requests.register_uri('POST', "%s/v2.0/tokens" % BASE_URI, - text=FAKE_ADMIN_TOKEN) + self.requests.post("%s/v2.0/tokens" % BASE_URI, text=FAKE_ADMIN_TOKEN) text = self.examples.SIGNED_REVOCATION_LIST - self.requests.register_uri('GET', "%s/v2.0/tokens/revoked" % BASE_URI, - response_list=[{'status_code': 401}, - {'text': text}]) + self.requests.get("%s/v2.0/tokens/revoked" % BASE_URI, + response_list=[{'status_code': 401}, {'text': text}]) fetched_list = jsonutils.loads(self.middleware.fetch_revocation_list()) self.assertEqual(fetched_list, self.examples.REVOCATION_LIST) @@ -291,17 +306,17 @@ class DiabloAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, super(DiabloAuthTokenMiddlewareTest, self).setUp( expected_env=expected_env) - self.requests.register_uri('GET', "%s/" % BASE_URI, - text=VERSION_LIST_v2, status_code=300) + self.requests.get("%s/" % BASE_URI, + text=VERSION_LIST_v2, + status_code=300) - self.requests.register_uri('POST', "%s/v2.0/tokens" % BASE_URI, - text=FAKE_ADMIN_TOKEN) + self.requests.post("%s/v2.0/tokens" % BASE_URI, text=FAKE_ADMIN_TOKEN) self.token_id = self.examples.VALID_DIABLO_TOKEN token_response = self.examples.JSON_TOKEN_RESPONSES[self.token_id] url = '%s/v2.0/tokens/%s' % (BASE_URI, self.token_id) - self.requests.register_uri('GET', url, text=token_response) + self.requests.get(url, text=token_response) self.set_middleware() @@ -856,8 +871,7 @@ class CommonAuthTokenMiddlewareTest(object): self.assertEqual(self.middleware.token_revocation_list, in_memory_list) def test_invalid_revocation_list_raises_service_error(self): - self.requests.register_uri('GET', '%s/v2.0/tokens/revoked' % BASE_URI, - text='{}') + self.requests.get('%s/v2.0/tokens/revoked' % BASE_URI, text='{}') self.assertRaises(auth_token.ServiceError, self.middleware.fetch_revocation_list) @@ -872,8 +886,7 @@ class CommonAuthTokenMiddlewareTest(object): # remember because we are testing the middleware we stub the connection # to the keystone server, but this is not what gets returned invalid_uri = "%s/v2.0/tokens/invalid-token" % BASE_URI - self.requests.register_uri('GET', invalid_uri, text="", - status_code=404) + self.requests.get(invalid_uri, text="", status_code=404) req = webob.Request.blank('/') req.headers['X-Auth-Token'] = 'invalid-token' @@ -961,7 +974,7 @@ class CommonAuthTokenMiddlewareTest(object): def test_memcache_set_invalid_uuid(self): invalid_uri = "%s/v2.0/tokens/invalid-token" % BASE_URI - self.requests.register_uri('GET', invalid_uri, status_code=404) + self.requests.get(invalid_uri, status_code=404) req = webob.Request.blank('/') token = 'invalid-token' @@ -1007,16 +1020,15 @@ class CommonAuthTokenMiddlewareTest(object): token = self.token_dict['signed_token_scoped'] req.headers['X-Auth-Token'] = token req.environ.update(extra_environ) - timeutils_utcnow = 'oslo.utils.timeutils.utcnow' + now = datetime.datetime.utcnow() - with mock.patch(timeutils_utcnow) as mock_utcnow: - mock_utcnow.return_value = now - self.middleware(req.environ, self.start_fake_response) - self.assertIsNotNone(self._get_cached_token(token)) - expired = now + datetime.timedelta(seconds=token_cache_time) - with mock.patch(timeutils_utcnow) as mock_utcnow: - mock_utcnow.return_value = expired - self.assertIsNone(self._get_cached_token(token)) + self.useFixture(TimeFixture(now)) + + self.middleware(req.environ, self.start_fake_response) + self.assertIsNotNone(self._get_cached_token(token)) + + timeutils.advance_time_seconds(token_cache_time) + self.assertIsNone(self._get_cached_token(token)) def test_swift_memcache_set_expired(self): extra_conf = {'cache': 'swift.cache'} @@ -1255,10 +1267,10 @@ class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, def test_request_no_token_dummy(self): cms._ensure_subprocess() - self.requests.register_uri('GET', "%s%s" % (BASE_URI, self.ca_path), - status_code=404) + self.requests.get("%s%s" % (BASE_URI, self.ca_path), + status_code=404) url = "%s%s" % (BASE_URI, self.signing_path) - self.requests.register_uri('GET', url, status_code=404) + self.requests.get(url, status_code=404) self.assertRaises(exceptions.CertificateConfigError, self.middleware.verify_signed_token, self.examples.SIGNED_TOKEN_SCOPED, @@ -1267,7 +1279,7 @@ class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, def test_fetch_signing_cert(self): data = 'FAKE CERT' url = '%s%s' % (BASE_URI, self.signing_path) - self.requests.register_uri('GET', url, text=data) + self.requests.get(url, text=data) self.middleware.fetch_signing_cert() with open(self.middleware.signing_cert_file_name, 'r') as f: @@ -1277,8 +1289,7 @@ class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, def test_fetch_signing_ca(self): data = 'FAKE CA' - self.requests.register_uri('GET', "%s%s" % (BASE_URI, self.ca_path), - text=data) + self.requests.get("%s%s" % (BASE_URI, self.ca_path), text=data) self.middleware.fetch_ca_cert() with open(self.middleware.signing_ca_file_name, 'r') as f: @@ -1293,11 +1304,10 @@ class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, self.conf['auth_port'] = 1234 self.conf['auth_admin_prefix'] = '/newadmin/' - self.requests.register_uri('GET', - "%s/newadmin%s" % (BASE_HOST, self.ca_path), - text='FAKECA') + self.requests.get("%s/newadmin%s" % (BASE_HOST, self.ca_path), + text='FAKECA') url = "%s/newadmin%s" % (BASE_HOST, self.signing_path) - self.requests.register_uri('GET', url, text='FAKECERT') + self.requests.get(url, text='FAKECERT') self.set_middleware(conf=self.conf) @@ -1316,11 +1326,9 @@ class V2CertDownloadMiddlewareTest(BaseAuthTokenMiddlewareTest, self.conf['auth_port'] = 1234 self.conf['auth_admin_prefix'] = '' - self.requests.register_uri('GET', "%s%s" % (BASE_HOST, self.ca_path), - text='FAKECA') - self.requests.register_uri('GET', "%s%s" % (BASE_HOST, - self.signing_path), - text='FAKECERT') + self.requests.get("%s%s" % (BASE_HOST, self.ca_path), text='FAKECA') + self.requests.get("%s%s" % (BASE_HOST, self.signing_path), + text='FAKECERT') self.set_middleware(conf=self.conf) @@ -1392,14 +1400,15 @@ class v2AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.examples.REVOKED_TOKEN_HASH_SHA256, } - self.requests.register_uri('GET', "%s/" % BASE_URI, - text=VERSION_LIST_v2, status_code=300) + self.requests.get("%s/" % BASE_URI, + text=VERSION_LIST_v2, + status_code=300) - self.requests.register_uri('POST', "%s/v2.0/tokens" % BASE_URI, - text=FAKE_ADMIN_TOKEN) + self.requests.post("%s/v2.0/tokens" % BASE_URI, + text=FAKE_ADMIN_TOKEN) - self.requests.register_uri('GET', "%s/v2.0/tokens/revoked" % BASE_URI, - text=self.examples.SIGNED_REVOCATION_LIST) + self.requests.get("%s/v2.0/tokens/revoked" % BASE_URI, + text=self.examples.SIGNED_REVOCATION_LIST) for token in (self.examples.UUID_TOKEN_DEFAULT, self.examples.UUID_TOKEN_UNSCOPED, @@ -1409,14 +1418,11 @@ class v2AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.examples.SIGNED_TOKEN_SCOPED_KEY, self.examples.SIGNED_TOKEN_SCOPED_PKIZ_KEY,): text = self.examples.JSON_TOKEN_RESPONSES[token] - self.requests.register_uri('GET', - '%s/v2.0/tokens/%s' % (BASE_URI, token), - text=text) + self.requests.get('%s/v2.0/tokens/%s' % (BASE_URI, token), + text=text) - self.requests.register_uri('GET', - '%s/v2.0/tokens/%s' % (BASE_URI, - ERROR_TOKEN), - text=network_error_response) + self.requests.get('%s/v2.0/tokens/%s' % (BASE_URI, ERROR_TOKEN), + text=network_error_response) self.set_middleware() @@ -1492,16 +1498,16 @@ class CrossVersionAuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, 'auth_version': 'v2.0' } - self.requests.register_uri('GET', '%s/' % BASE_URI, - text=VERSION_LIST_v3, status_code=300) + self.requests.get('%s/' % BASE_URI, + text=VERSION_LIST_v3, + status_code=300) - self.requests.register_uri('POST', '%s/v2.0/tokens' % BASE_URI, - text=FAKE_ADMIN_TOKEN) + self.requests.post('%s/v2.0/tokens' % BASE_URI, text=FAKE_ADMIN_TOKEN) token = self.examples.UUID_TOKEN_DEFAULT url = '%s/v2.0/tokens/%s' % (BASE_URI, token) response_body = self.examples.JSON_TOKEN_RESPONSES[token] - self.requests.register_uri('GET', url, text=response_body) + self.requests.get(url, text=response_body) self.set_middleware(conf=conf) @@ -1573,20 +1579,18 @@ class v3AuthTokenMiddlewareTest(BaseAuthTokenMiddlewareTest, self.examples.REVOKED_v3_PKIZ_TOKEN_HASH, } - self.requests.register_uri('GET', BASE_URI, - text=VERSION_LIST_v3, status_code=300) + self.requests.get(BASE_URI, text=VERSION_LIST_v3, status_code=300) # TODO(jamielennox): auth_token middleware uses a v2 admin token # regardless of the auth_version that is set. - self.requests.register_uri('POST', '%s/v2.0/tokens' % BASE_URI, - text=FAKE_ADMIN_TOKEN) + self.requests.post('%s/v2.0/tokens' % BASE_URI, text=FAKE_ADMIN_TOKEN) # TODO(jamielennox): there is no v3 revocation url yet, it uses v2 - self.requests.register_uri('GET', '%s/v2.0/tokens/revoked' % BASE_URI, - text=self.examples.SIGNED_REVOCATION_LIST) + self.requests.get('%s/v2.0/tokens/revoked' % BASE_URI, + text=self.examples.SIGNED_REVOCATION_LIST) - self.requests.register_uri('GET', '%s/v3/auth/tokens' % BASE_URI, - text=self.token_response) + self.requests.get('%s/v3/auth/tokens' % BASE_URI, + text=self.token_response) self.set_middleware() @@ -1759,22 +1763,16 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): auth_token.confirm_token_not_expired, data) - @mock.patch('oslo.utils.timeutils.utcnow') - def test_v2_token_with_timezone_offset_not_expired(self, mock_utcnow): - current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') - current_time = timeutils.normalize_time(current_time) - mock_utcnow.return_value = current_time + def test_v2_token_with_timezone_offset_not_expired(self): + self.useFixture(TimeFixture('2000-01-01T00:01:10.000123Z')) data = self.create_v2_token_fixture( expires='2000-01-01T00:05:10.000123-05:00') expected_expires = '2000-01-01T05:05:10.000123Z' actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) - @mock.patch('oslo.utils.timeutils.utcnow') - def test_v2_token_with_timezone_offset_expired(self, mock_utcnow): - current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') - current_time = timeutils.normalize_time(current_time) - mock_utcnow.return_value = current_time + def test_v2_token_with_timezone_offset_expired(self): + self.useFixture(TimeFixture('2000-01-01T00:01:10.000123Z')) data = self.create_v2_token_fixture( expires='2000-01-01T00:05:10.000123+05:00') data['access']['token']['expires'] = '2000-01-01T00:05:10.000123+05:00' @@ -1794,11 +1792,8 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): auth_token.confirm_token_not_expired, data) - @mock.patch('oslo.utils.timeutils.utcnow') - def test_v3_token_with_timezone_offset_not_expired(self, mock_utcnow): - current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') - current_time = timeutils.normalize_time(current_time) - mock_utcnow.return_value = current_time + def test_v3_token_with_timezone_offset_not_expired(self): + self.useFixture(TimeFixture('2000-01-01T00:01:10.000123Z')) data = self.create_v3_token_fixture( expires='2000-01-01T00:05:10.000123-05:00') expected_expires = '2000-01-01T05:05:10.000123Z' @@ -1806,11 +1801,8 @@ class TokenExpirationTest(BaseAuthTokenMiddlewareTest): actual_expires = auth_token.confirm_token_not_expired(data) self.assertEqual(actual_expires, expected_expires) - @mock.patch('oslo.utils.timeutils.utcnow') - def test_v3_token_with_timezone_offset_expired(self, mock_utcnow): - current_time = timeutils.parse_isotime('2000-01-01T00:01:10.000123Z') - current_time = timeutils.normalize_time(current_time) - mock_utcnow.return_value = current_time + def test_v3_token_with_timezone_offset_expired(self): + self.useFixture(TimeFixture('2000-01-01T00:01:10.000123Z')) data = self.create_v3_token_fixture( expires='2000-01-01T00:05:10.000123+05:00') self.assertRaises(auth_token.InvalidUserToken, diff --git a/keystoneclient/tests/test_discovery.py b/keystoneclient/tests/test_discovery.py index a999a19..f9d3df2 100644 --- a/keystoneclient/tests/test_discovery.py +++ b/keystoneclient/tests/test_discovery.py @@ -242,7 +242,7 @@ class AvailableVersionsTests(utils.TestCase): for path, text in six.iteritems(examples): url = "%s%s" % (BASE_URL, path) - self.requests.register_uri('GET', url, status_code=300, text=text) + self.requests.get(url, status_code=300, text=text) versions = discover.available_versions(url) for v in versions: @@ -252,8 +252,7 @@ class AvailableVersionsTests(utils.TestCase): matchers.Contains(n))) def test_available_versions_individual(self): - self.requests.register_uri('GET', V3_URL, status_code=200, - text=V3_VERSION_ENTRY) + self.requests.get(V3_URL, status_code=200, text=V3_VERSION_ENTRY) versions = discover.available_versions(V3_URL) @@ -264,8 +263,7 @@ class AvailableVersionsTests(utils.TestCase): self.assertIn('links', v) def test_available_keystone_data(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) versions = discover.available_versions(BASE_URL) self.assertEqual(2, len(versions)) @@ -280,7 +278,7 @@ class AvailableVersionsTests(utils.TestCase): def test_available_cinder_data(self): text = jsonutils.dumps(CINDER_EXAMPLES) - self.requests.register_uri('GET', BASE_URL, status_code=300, text=text) + self.requests.get(BASE_URL, status_code=300, text=text) versions = discover.available_versions(BASE_URL) self.assertEqual(2, len(versions)) @@ -296,7 +294,7 @@ class AvailableVersionsTests(utils.TestCase): def test_available_glance_data(self): text = jsonutils.dumps(GLANCE_EXAMPLES) - self.requests.register_uri('GET', BASE_URL, status_code=200, text=text) + self.requests.get(BASE_URL, status_code=200, text=text) versions = discover.available_versions(BASE_URL) self.assertEqual(5, len(versions)) @@ -313,10 +311,9 @@ class AvailableVersionsTests(utils.TestCase): class ClientDiscoveryTests(utils.TestCase): def assertCreatesV3(self, **kwargs): - self.requests.register_uri('POST', - '%s/auth/tokens' % V3_URL, - text=V3_AUTH_RESPONSE, - headers={'X-Subject-Token': V3_TOKEN}) + self.requests.post('%s/auth/tokens' % V3_URL, + text=V3_AUTH_RESPONSE, + headers={'X-Subject-Token': V3_TOKEN}) kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') @@ -325,8 +322,7 @@ class ClientDiscoveryTests(utils.TestCase): return keystone def assertCreatesV2(self, **kwargs): - self.requests.register_uri('POST', "%s/tokens" % V2_URL, - text=V2_AUTH_RESPONSE) + self.requests.post("%s/tokens" % V2_URL, text=V2_AUTH_RESPONSE) kwargs.setdefault('username', 'foo') kwargs.setdefault('password', 'bar') @@ -349,89 +345,73 @@ class ClientDiscoveryTests(utils.TestCase): client.Client, **kwargs) def test_discover_v3(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) self.assertCreatesV3(auth_url=BASE_URL) def test_discover_v2(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V2_VERSION_LIST) - self.requests.register_uri('POST', "%s/tokens" % V2_URL, - text=V2_AUTH_RESPONSE) + self.requests.get(BASE_URL, status_code=300, text=V2_VERSION_LIST) + self.requests.post("%s/tokens" % V2_URL, text=V2_AUTH_RESPONSE) self.assertCreatesV2(auth_url=BASE_URL) def test_discover_endpoint_v2(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V2_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V2_VERSION_LIST) self.assertCreatesV2(endpoint=BASE_URL, token='fake-token') def test_discover_endpoint_v3(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) self.assertCreatesV3(endpoint=BASE_URL, token='fake-token') def test_discover_invalid_major_version(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) self.assertVersionNotAvailable(auth_url=BASE_URL, version=5) def test_discover_200_response_fails(self): - self.requests.register_uri('GET', BASE_URL, - status_code=200, text='ok') + self.requests.get(BASE_URL, text='ok') self.assertDiscoveryFailure(auth_url=BASE_URL) def test_discover_minor_greater_than_available_fails(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) self.assertVersionNotAvailable(endpoint=BASE_URL, version=3.4) def test_discover_individual_version_v2(self): - self.requests.register_uri('GET', V2_URL, status_code=200, - text=V2_VERSION_ENTRY) + self.requests.get(V2_URL, text=V2_VERSION_ENTRY) self.assertCreatesV2(auth_url=V2_URL) def test_discover_individual_version_v3(self): - self.requests.register_uri('GET', V3_URL, status_code=200, - text=V3_VERSION_ENTRY) + self.requests.get(V3_URL, text=V3_VERSION_ENTRY) self.assertCreatesV3(auth_url=V3_URL) def test_discover_individual_endpoint_v2(self): - self.requests.register_uri('GET', V2_URL, status_code=200, - text=V2_VERSION_ENTRY) + self.requests.get(V2_URL, text=V2_VERSION_ENTRY) self.assertCreatesV2(endpoint=V2_URL, token='fake-token') def test_discover_individual_endpoint_v3(self): - self.requests.register_uri('GET', V3_URL, status_code=200, - text=V3_VERSION_ENTRY) + self.requests.get(V3_URL, text=V3_VERSION_ENTRY) self.assertCreatesV3(endpoint=V3_URL, token='fake-token') def test_discover_fail_to_create_bad_individual_version(self): - self.requests.register_uri('GET', V2_URL, status_code=200, - text=V2_VERSION_ENTRY) - self.requests.register_uri('GET', V3_URL, status_code=200, - text=V3_VERSION_ENTRY) + self.requests.get(V2_URL, text=V2_VERSION_ENTRY) + self.requests.get(V3_URL, text=V3_VERSION_ENTRY) self.assertVersionNotAvailable(auth_url=V2_URL, version=3) self.assertVersionNotAvailable(auth_url=V3_URL, version=2) def test_discover_unstable_versions(self): version_list = fixture.DiscoveryList(BASE_URL, v3_status='beta') - self.requests.register_uri('GET', BASE_URL, status_code=300, - json=version_list) + self.requests.get(BASE_URL, status_code=300, json=version_list) self.assertCreatesV2(auth_url=BASE_URL) self.assertVersionNotAvailable(auth_url=BASE_URL, version=3) self.assertCreatesV3(auth_url=BASE_URL, unstable=True) def test_discover_forwards_original_ip(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) ip = '192.168.1.1' self.assertCreatesV3(auth_url=BASE_URL, original_ip=ip) @@ -444,8 +424,7 @@ class ClientDiscoveryTests(utils.TestCase): client.Client) def test_discover_bad_response(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - json={'FOO': 'BAR'}) + self.requests.get(BASE_URL, status_code=300, json={'FOO': 'BAR'}) self.assertDiscoveryFailure(auth_url=BASE_URL) def test_discovery_ignore_invalid(self): @@ -454,44 +433,40 @@ class ClientDiscoveryTests(utils.TestCase): 'media-types': V3_MEDIA_TYPES, 'status': 'stable', 'updated': UPDATED}] - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=_create_version_list(resp)) + self.requests.get(BASE_URL, status_code=300, + text=_create_version_list(resp)) self.assertDiscoveryFailure(auth_url=BASE_URL) def test_ignore_entry_without_links(self): v3 = V3_VERSION.copy() v3['links'] = [] - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=_create_version_list([v3, V2_VERSION])) + self.requests.get(BASE_URL, status_code=300, + text=_create_version_list([v3, V2_VERSION])) self.assertCreatesV2(auth_url=BASE_URL) def test_ignore_entry_without_status(self): v3 = V3_VERSION.copy() del v3['status'] - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=_create_version_list([v3, V2_VERSION])) + self.requests.get(BASE_URL, status_code=300, + text=_create_version_list([v3, V2_VERSION])) self.assertCreatesV2(auth_url=BASE_URL) def test_greater_version_than_required(self): versions = fixture.DiscoveryList(BASE_URL, v3_id='v3.6') - self.requests.register_uri('GET', BASE_URL, status_code=200, - json=versions) + self.requests.get(BASE_URL, json=versions) self.assertCreatesV3(auth_url=BASE_URL, version=(3, 4)) def test_lesser_version_than_required(self): versions = fixture.DiscoveryList(BASE_URL, v3_id='v3.4') - self.requests.register_uri('GET', BASE_URL, status_code=200, - json=versions) + self.requests.get(BASE_URL, json=versions) self.assertVersionNotAvailable(auth_url=BASE_URL, version=(3, 6)) def test_bad_response(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text="Ugly Duckling") + self.requests.get(BASE_URL, status_code=300, text="Ugly Duckling") self.assertDiscoveryFailure(auth_url=BASE_URL) def test_pass_client_arguments(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V2_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V2_VERSION_LIST) kwargs = {'original_ip': '100', 'use_keyring': False, 'stale_duration': 15} @@ -502,12 +477,11 @@ class ClientDiscoveryTests(utils.TestCase): self.assertFalse(cl.use_keyring) def test_overriding_stored_kwargs(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) - self.requests.register_uri('POST', "%s/auth/tokens" % V3_URL, - text=V3_AUTH_RESPONSE, - headers={'X-Subject-Token': V3_TOKEN}) + self.requests.post("%s/auth/tokens" % V3_URL, + text=V3_AUTH_RESPONSE, + headers={'X-Subject-Token': V3_TOKEN}) disc = discover.Discover(auth_url=BASE_URL, debug=False, username='foo') @@ -520,8 +494,7 @@ class ClientDiscoveryTests(utils.TestCase): self.assertEqual(client.password, 'bar') def test_available_versions(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_ENTRY) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_ENTRY) disc = discover.Discover(auth_url=BASE_URL) versions = disc.available_versions() @@ -536,8 +509,7 @@ class ClientDiscoveryTests(utils.TestCase): 'updated': UPDATED} versions = fixture.DiscoveryList() versions.add_version(V4_VERSION) - self.requests.register_uri('GET', BASE_URL, status_code=300, - json=versions) + self.requests.get(BASE_URL, status_code=300, json=versions) disc = discover.Discover(auth_url=BASE_URL) self.assertRaises(exceptions.DiscoveryFailure, @@ -545,16 +517,14 @@ class ClientDiscoveryTests(utils.TestCase): def test_discovery_fail_for_missing_v3(self): versions = fixture.DiscoveryList(v2=True, v3=False) - self.requests.register_uri('GET', BASE_URL, status_code=300, - json=versions) + self.requests.get(BASE_URL, status_code=300, json=versions) disc = discover.Discover(auth_url=BASE_URL) self.assertRaises(exceptions.DiscoveryFailure, disc.create_client, version=(3, 0)) def _do_discovery_call(self, token=None, **kwargs): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) if not token: token = uuid.uuid4().hex @@ -581,8 +551,7 @@ class ClientDiscoveryTests(utils.TestCase): class DiscoverQueryTests(utils.TestCase): def test_available_keystone_data(self): - self.requests.register_uri('GET', BASE_URL, status_code=300, - text=V3_VERSION_LIST) + self.requests.get(BASE_URL, status_code=300, text=V3_VERSION_LIST) disc = discover.Discover(auth_url=BASE_URL) versions = disc.version_data() @@ -610,7 +579,7 @@ class DiscoverQueryTests(utils.TestCase): def test_available_cinder_data(self): text = jsonutils.dumps(CINDER_EXAMPLES) - self.requests.register_uri('GET', BASE_URL, status_code=300, text=text) + self.requests.get(BASE_URL, status_code=300, text=text) v1_url = "%sv1/" % BASE_URL v2_url = "%sv2/" % BASE_URL @@ -641,7 +610,7 @@ class DiscoverQueryTests(utils.TestCase): def test_available_glance_data(self): text = jsonutils.dumps(GLANCE_EXAMPLES) - self.requests.register_uri('GET', BASE_URL, status_code=200, text=text) + self.requests.get(BASE_URL, text=text) v1_url = "%sv1/" % BASE_URL v2_url = "%sv2/" % BASE_URL @@ -690,7 +659,7 @@ class DiscoverQueryTests(utils.TestCase): 'status': status, 'updated': UPDATED}] text = jsonutils.dumps({'versions': version_list}) - self.requests.register_uri('GET', BASE_URL, status_code=200, text=text) + self.requests.get(BASE_URL, text=text) disc = discover.Discover(auth_url=BASE_URL) @@ -712,7 +681,7 @@ class DiscoverQueryTests(utils.TestCase): 'status': status, 'updated': UPDATED}] text = jsonutils.dumps({'versions': version_list}) - self.requests.register_uri('GET', BASE_URL, status_code=200, text=text) + self.requests.get(BASE_URL, text=text) disc = discover.Discover(auth_url=BASE_URL) @@ -729,8 +698,7 @@ class DiscoverQueryTests(utils.TestCase): status = 'abcdef' version_list = fixture.DiscoveryList(BASE_URL, v2=False, v3_status=status) - self.requests.register_uri('GET', BASE_URL, status_code=200, - json=version_list) + self.requests.get(BASE_URL, json=version_list) disc = discover.Discover(auth_url=BASE_URL) versions = disc.version_data() @@ -759,8 +727,7 @@ class DiscoverQueryTests(utils.TestCase): }] text = jsonutils.dumps({'versions': version_list}) - self.requests.register_uri('GET', BASE_URL, status_code=200, - text=text) + self.requests.get(BASE_URL, text=text) disc = discover.Discover(auth_url=BASE_URL) diff --git a/keystoneclient/tests/test_s3_token_middleware.py b/keystoneclient/tests/test_s3_token_middleware.py index ab77b79..25fc29e 100644 --- a/keystoneclient/tests/test_s3_token_middleware.py +++ b/keystoneclient/tests/test_s3_token_middleware.py @@ -64,8 +64,7 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): super(S3TokenMiddlewareTestGood, self).setUp() self.middleware = s3_token.S3Token(FakeApp(), self.conf) - self.requests.register_uri('POST', self.TEST_URL, - status_code=201, json=GOOD_RESPONSE) + self.requests.post(self.TEST_URL, status_code=201, json=GOOD_RESPONSE) # Ignore the request and pass to the next middleware in the # pipeline if no path has been specified. @@ -99,8 +98,7 @@ class S3TokenMiddlewareTestGood(S3TokenMiddlewareTestBase): TEST_URL = 'http://%s:%d/v2.0/s3tokens' % (self.TEST_HOST, self.TEST_PORT) - self.requests.register_uri('POST', TEST_URL, - status_code=201, json=GOOD_RESPONSE) + self.requests.post(TEST_URL, status_code=201, json=GOOD_RESPONSE) self.middleware = ( s3_token.filter_factory({'auth_protocol': 'http', @@ -153,8 +151,7 @@ class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase): {"message": "EC2 access key not found.", "code": 401, "title": "Unauthorized"}} - self.requests.register_uri('POST', self.TEST_URL, - status_code=403, json=ret) + self.requests.post(self.TEST_URL, status_code=403, json=ret) req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' req.headers['X-Storage-Token'] = 'token' @@ -186,8 +183,7 @@ class S3TokenMiddlewareTestBad(S3TokenMiddlewareTestBase): self.assertEqual(resp.status_int, s3_invalid_req.status_int) def test_bad_reply(self): - self.requests.register_uri('POST', self.TEST_URL, - status_code=201, text="<badreply>") + self.requests.post(self.TEST_URL, status_code=201, text="<badreply>") req = webob.Request.blank('/v1/AUTH_cfa/c/o') req.headers['Authorization'] = 'access:signature' diff --git a/keystoneclient/tests/test_session.py b/keystoneclient/tests/test_session.py index 8aa5a1e..d3d964c 100644 --- a/keystoneclient/tests/test_session.py +++ b/keystoneclient/tests/test_session.py @@ -321,6 +321,8 @@ class AuthPlugin(base.BaseAuthPlugin): """ TEST_TOKEN = 'aToken' + TEST_USER_ID = 'aUser' + TEST_PROJECT_ID = 'aProject' SERVICE_URLS = { 'identity': {'public': 'http://identity-public:1111/v2.0', @@ -348,6 +350,12 @@ class AuthPlugin(base.BaseAuthPlugin): def invalidate(self): return self._invalidate + def get_user_id(self, session): + return self.TEST_USER_ID + + def get_project_id(self, session): + return self.TEST_PROJECT_ID + class CalledAuthPlugin(base.BaseAuthPlugin): @@ -447,7 +455,7 @@ class SessionAuthTests(utils.TestCase): def test_raises_exc_only_when_asked(self): # A request that returns a HTTP error should by default raise an # exception by default, if you specify raise_exc=False then it will not - self.requests.register_uri('GET', self.TEST_URL, status_code=401) + self.requests.get(self.TEST_URL, status_code=401) sess = client_session.Session() self.assertRaises(exceptions.Unauthorized, sess.get, self.TEST_URL) @@ -459,9 +467,8 @@ class SessionAuthTests(utils.TestCase): passed = CalledAuthPlugin() sess = client_session.Session() - self.requests.register_uri('GET', - CalledAuthPlugin.ENDPOINT + 'path', - status_code=200) + self.requests.get(CalledAuthPlugin.ENDPOINT + 'path', + status_code=200) endpoint_filter = {'service_type': 'identity'} # no plugin with authenticated won't work @@ -484,9 +491,8 @@ class SessionAuthTests(utils.TestCase): sess = client_session.Session(fixed) - self.requests.register_uri('GET', - CalledAuthPlugin.ENDPOINT + 'path', - status_code=200) + self.requests.get(CalledAuthPlugin.ENDPOINT + 'path', + status_code=200) resp = sess.get('path', auth=passed, endpoint_filter={'service_type': 'identity'}) @@ -518,9 +524,9 @@ class SessionAuthTests(utils.TestCase): auth = CalledAuthPlugin(invalidate=True) sess = client_session.Session(auth=auth) - self.requests.register_uri('GET', self.TEST_URL, - [{'text': 'Failed', 'status_code': 401}, - {'text': 'Hello', 'status_code': 200}]) + self.requests.get(self.TEST_URL, + [{'text': 'Failed', 'status_code': 401}, + {'text': 'Hello', 'status_code': 200}]) # allow_reauth=True is the default resp = sess.get(self.TEST_URL, authenticated=True) @@ -533,9 +539,9 @@ class SessionAuthTests(utils.TestCase): auth = CalledAuthPlugin(invalidate=True) sess = client_session.Session(auth=auth) - self.requests.register_uri('GET', self.TEST_URL, - [{'text': 'Failed', 'status_code': 401}, - {'text': 'Hello', 'status_code': 200}]) + self.requests.get(self.TEST_URL, + [{'text': 'Failed', 'status_code': 401}, + {'text': 'Hello', 'status_code': 200}]) self.assertRaises(exceptions.Unauthorized, sess.get, self.TEST_URL, authenticated=True, allow_reauth=False) @@ -550,7 +556,7 @@ class SessionAuthTests(utils.TestCase): override_url = override_base + path resp_text = uuid.uuid4().hex - self.requests.register_uri('GET', override_url, text=resp_text) + self.requests.get(override_url, text=resp_text) resp = sess.get(path, endpoint_override=override_base, @@ -570,7 +576,7 @@ class SessionAuthTests(utils.TestCase): url = self.TEST_URL + path resp_text = uuid.uuid4().hex - self.requests.register_uri('GET', url, text=resp_text) + self.requests.get(url, text=resp_text) resp = sess.get(url, endpoint_override='http://someother.url', @@ -582,6 +588,13 @@ class SessionAuthTests(utils.TestCase): self.assertTrue(auth.get_token_called) self.assertFalse(auth.get_endpoint_called) + def test_user_and_project_id(self): + auth = AuthPlugin() + sess = client_session.Session(auth=auth) + + self.assertEqual(auth.TEST_USER_ID, sess.get_user_id()) + self.assertEqual(auth.TEST_PROJECT_ID, sess.get_project_id()) + class AdapterTest(utils.TestCase): @@ -692,13 +705,15 @@ class AdapterTest(utils.TestCase): adpt = adapter.Adapter(sess, endpoint_override=endpoint_override) response = uuid.uuid4().hex - self.requests.register_uri('GET', endpoint_url, text=response) + self.requests.get(endpoint_url, text=response) resp = adpt.get(path) self.assertEqual(response, resp.text) self.assertEqual(endpoint_url, self.requests.last_request.url) + self.assertEqual(endpoint_override, adpt.get_endpoint()) + def test_adapter_invalidate(self): auth = CalledAuthPlugin() sess = client_session.Session() @@ -735,6 +750,14 @@ class AdapterTest(utils.TestCase): self.assertThat(self.requests.request_history, matchers.HasLength(retries + 1)) + def test_user_and_project_id(self): + auth = AuthPlugin() + sess = client_session.Session() + adpt = adapter.Adapter(sess, auth=auth) + + self.assertEqual(auth.TEST_USER_ID, adpt.get_user_id()) + self.assertEqual(auth.TEST_PROJECT_ID, adpt.get_project_id()) + class ConfLoadingTests(utils.TestCase): diff --git a/keystoneclient/tests/test_shell.py b/keystoneclient/tests/test_shell.py index 4be563b..65cba9d 100644 --- a/keystoneclient/tests/test_shell.py +++ b/keystoneclient/tests/test_shell.py @@ -37,6 +37,11 @@ DEFAULT_TENANT_NAME = 'tenant_name' DEFAULT_AUTH_URL = 'http://127.0.0.1:5000/v2.0/' +# Make a fake shell object, a helping wrapper to call it +def shell(cmd): + openstack_shell.OpenStackIdentityShell().main(cmd.split()) + + class NoExitArgumentParser(argparse.ArgumentParser): def error(self, message): raise exceptions.CommandError(message) @@ -68,12 +73,6 @@ class ShellTest(utils.TestCase): self.useFixture(fixtures.EnvironmentVariable(var, self.FAKE_ENV[var])) - # Make a fake shell object, a helping wrapper to call it, and a quick - # way of asserting that certain API calls were made. - global shell, _shell, assert_called, assert_called_anytime - _shell = openstack_shell.OpenStackIdentityShell() - shell = lambda cmd: _shell.main(cmd.split()) - def test_help_unknown_command(self): self.assertRaises(exceptions.CommandError, shell, 'help %s' % uuid.uuid4().hex) diff --git a/keystoneclient/tests/v2_0/test_certificates.py b/keystoneclient/tests/v2_0/test_certificates.py new file mode 100644 index 0000000..432a304 --- /dev/null +++ b/keystoneclient/tests/v2_0/test_certificates.py @@ -0,0 +1,40 @@ +# Copyright 2014 IBM Corp. +# 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 testresources + +from keystoneclient.tests import client_fixtures +from keystoneclient.tests.v2_0 import utils + + +class CertificateTests(utils.TestCase, testresources.ResourcedTestCase): + + resources = [('examples', client_fixtures.EXAMPLES_RESOURCE)] + + def test_get_ca_certificate(self): + self.stub_url('GET', ['certificates', 'ca'], + headers={'Content-Type': 'text/html; charset=UTF-8'}, + text=self.examples.SIGNING_CA) + res = self.client.certificates.get_ca_certificate() + self.assertEqual(self.examples.SIGNING_CA, res) + + def test_get_signing_certificate(self): + self.stub_url('GET', ['certificates', 'signing'], + headers={'Content-Type': 'text/html; charset=UTF-8'}, + text=self.examples.SIGNING_CERT) + res = self.client.certificates.get_signing_certificate() + self.assertEqual(self.examples.SIGNING_CERT, res) + + +def load_tests(loader, tests, pattern): + return testresources.OptimisingTestSuite(tests) diff --git a/keystoneclient/tests/v2_0/test_client.py b/keystoneclient/tests/v2_0/test_client.py index 5fbedf4..d09f326 100644 --- a/keystoneclient/tests/v2_0/test_client.py +++ b/keystoneclient/tests/v2_0/test_client.py @@ -27,7 +27,8 @@ from keystoneclient.v2_0 import client class KeystoneClientTest(utils.TestCase): def test_unscoped_init(self): - self.stub_auth(json=client_fixtures.unscoped_token()) + token = client_fixtures.unscoped_token() + self.stub_auth(json=token) c = client.Client(username='exampleuser', password='password', @@ -38,9 +39,12 @@ class KeystoneClientTest(utils.TestCase): self.assertFalse(c.auth_ref.project_scoped) self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) + self.assertIsNone(c.get_project_id(session=None)) + self.assertEqual(token.user_id, c.get_user_id(session=None)) def test_scoped_init(self): - self.stub_auth(json=client_fixtures.project_scoped_token()) + token = client_fixtures.project_scoped_token() + self.stub_auth(json=token) c = client.Client(username='exampleuser', password='password', @@ -53,6 +57,9 @@ class KeystoneClientTest(utils.TestCase): self.assertIsNone(c.auth_ref.trust_id) self.assertFalse(c.auth_ref.trust_scoped) + self.assertEqual(token.tenant_id, c.get_project_id(session=None)) + self.assertEqual(token.user_id, c.get_user_id(session=None)) + def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.project_scoped_token()) diff --git a/keystoneclient/tests/v2_0/test_shell.py b/keystoneclient/tests/v2_0/test_shell.py index 57bbe9d..44943a0 100644 --- a/keystoneclient/tests/v2_0/test_shell.py +++ b/keystoneclient/tests/v2_0/test_shell.py @@ -14,6 +14,7 @@ import os import sys import mock +from oslo.serialization import jsonutils import six from testtools import matchers @@ -268,16 +269,27 @@ class ShellTests(utils.TestCase): self.run_command('tenant-delete 2') self.assert_called('DELETE', '/tenants/2') - def test_service_create(self): + def test_service_create_with_required_arguments_only(self): self.stub_url('POST', ['OS-KSADM', 'services'], json={'OS-KSADM:service': {}}) - self.run_command('service-create --name service1 --type compute') + self.run_command('service-create --type compute') self.assert_called('POST', '/OS-KSADM/services') json = {"OS-KSADM:service": {"type": "compute", - "name": "service1", + "name": None, "description": None}} self.assertRequestBodyIs(json=json) + def test_service_create_with_all_arguments(self): + self.stub_url('POST', ['OS-KSADM', 'services'], + json={'OS-KSADM:service': {}}) + self.run_command('service-create --type compute ' + '--name service1 --description desc1') + self.assert_called('POST', '/OS-KSADM/services') + json = {"OS-KSADM:service": {"type": "compute", + "name": "service1", + "description": "desc1"}} + self.assertRequestBodyIs(json=json) + def test_service_get(self): self.stub_url('GET', ['OS-KSADM', 'services', '1'], json={'OS-KSADM:service': {'id': '1'}}) @@ -381,22 +393,21 @@ class ShellTests(utils.TestCase): ' --tenant-name new-tenant') def called_anytime(method, path, json=None): + test_url = self.TEST_URL.strip('/') for r in self.requests.request_history: if not r.method == method: continue - if not r.url == self.TEST_URL + path: + if not r.url == test_url + path: continue if json: - last_request_body = r.body.decode('utf-8') - json_body = jsonutils.loads(last_request_body) - + json_body = jsonutils.loads(r.body) if not json_body == json: continue return True - return False + raise AssertionError('URL never called') called_anytime('POST', '/users', {'user': {'email': None, 'password': '1', @@ -409,7 +420,7 @@ class ShellTests(utils.TestCase): "description": None}}) called_anytime('POST', '/OS-KSADM/roles', - {"role": {"name": "new-role"}}) + {"role": {"name": "admin"}}) called_anytime('PUT', '/tenants/1/users/1/roles/OS-KSADM/1') diff --git a/keystoneclient/tests/v2_0/test_tokens.py b/keystoneclient/tests/v2_0/test_tokens.py index 688972b..3351273 100644 --- a/keystoneclient/tests/v2_0/test_tokens.py +++ b/keystoneclient/tests/v2_0/test_tokens.py @@ -160,3 +160,10 @@ class TokenTests(utils.TestCase): self.assertIsInstance(token_ref, tokens.Token) self.assertEqual(token_fixture.token_id, token_ref.id) self.assertEqual(token_fixture.expires_str, token_ref.expires) + + def test_get_revoked(self): + sample_revoked_response = {'signed': '-----BEGIN CMS-----\nMIIB...'} + self.stub_url('GET', ['tokens', 'revoked'], + json=sample_revoked_response) + resp = self.client.tokens.get_revoked() + self.assertEqual(sample_revoked_response, resp) diff --git a/keystoneclient/tests/v3/test_auth_saml2.py b/keystoneclient/tests/v3/test_auth_saml2.py index bdb7a87..f9a0776 100644 --- a/keystoneclient/tests/v3/test_auth_saml2.py +++ b/keystoneclient/tests/v3/test_auth_saml2.py @@ -128,8 +128,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): def test_initial_sp_call(self): """Test initial call, expect SOAP message.""" - self.requests.register_uri( - 'GET', + self.requests.get( self.FEDERATION_AUTH_URL, content=make_oneline(saml2_fixtures.SP_SOAP_RESPONSE)) a = self.saml2plugin._send_service_provider_request(self.session) @@ -154,8 +153,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): str(self.saml2plugin.sp_response_consumer_url))) def test_initial_sp_call_when_saml_authenticated(self): - self.requests.register_uri( - 'GET', + self.requests.get( self.FEDERATION_AUTH_URL, json=saml2_fixtures.UNSCOPED_TOKEN, headers={'X-Subject-Token': saml2_fixtures.UNSCOPED_TOKEN_HEADER}) @@ -170,8 +168,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.saml2plugin.authenticated_response.headers['X-Subject-Token']) def test_get_unscoped_token_when_authenticated(self): - self.requests.register_uri( - 'GET', + self.requests.get( self.FEDERATION_AUTH_URL, json=saml2_fixtures.UNSCOPED_TOKEN, headers={'X-Subject-Token': saml2_fixtures.UNSCOPED_TOKEN_HEADER, @@ -184,9 +181,8 @@ class AuthenticateviaSAML2Tests(utils.TestCase): def test_initial_sp_call_invalid_response(self): """Send initial SP HTTP request and receive wrong server response.""" - self.requests.register_uri('GET', - self.FEDERATION_AUTH_URL, - text='NON XML RESPONSE') + self.requests.get(self.FEDERATION_AUTH_URL, + text='NON XML RESPONSE') self.assertRaises( exceptions.AuthorizationFailure, @@ -194,9 +190,8 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.session) def test_send_authn_req_to_idp(self): - self.requests.register_uri('POST', - self.IDENTITY_PROVIDER_URL, - content=saml2_fixtures.SAML2_ASSERTION) + self.requests.post(self.IDENTITY_PROVIDER_URL, + content=saml2_fixtures.SAML2_ASSERTION) self.saml2plugin.sp_response_consumer_url = self.SHIB_CONSUMER_URL self.saml2plugin.saml2_authn_request = etree.XML( @@ -213,9 +208,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.assertEqual(idp_response, saml2_assertion_oneline, error) def test_fail_basicauth_idp_authentication(self): - self.requests.register_uri('POST', - self.IDENTITY_PROVIDER_URL, - status_code=401) + self.requests.post(self.IDENTITY_PROVIDER_URL, status_code=401) self.saml2plugin.sp_response_consumer_url = self.SHIB_CONSUMER_URL self.saml2plugin.saml2_authn_request = etree.XML( @@ -232,8 +225,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.IDENTITY_PROVIDER_URL) def test_send_authn_response_to_sp(self): - self.requests.register_uri( - 'POST', + self.requests.post( self.SHIB_CONSUMER_URL, json=saml2_fixtures.UNSCOPED_TOKEN, headers={'X-Subject-Token': saml2_fixtures.UNSCOPED_TOKEN_HEADER}) @@ -263,7 +255,7 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.SHIB_CONSUMER_URL) def test_consumer_url_mismatch(self): - self.requests.register_uri('POST', self.SHIB_CONSUMER_URL) + self.requests.post(self.SHIB_CONSUMER_URL) invalid_consumer_url = uuid.uuid4().hex self.assertRaises( exceptions.ValidationError, @@ -272,15 +264,13 @@ class AuthenticateviaSAML2Tests(utils.TestCase): invalid_consumer_url) def test_custom_302_redirection(self): - self.requests.register_uri( - 'POST', + self.requests.post( self.SHIB_CONSUMER_URL, text='BODY', headers={'location': self.FEDERATION_AUTH_URL}, status_code=302) - self.requests.register_uri( - 'GET', + self.requests.get( self.FEDERATION_AUTH_URL, json=saml2_fixtures.UNSCOPED_TOKEN, headers={'X-Subject-Token': saml2_fixtures.UNSCOPED_TOKEN_HEADER}) @@ -299,17 +289,14 @@ class AuthenticateviaSAML2Tests(utils.TestCase): self.assertEqual('GET', response.request.method) def test_end_to_end_workflow(self): - self.requests.register_uri( - 'GET', + self.requests.get( self.FEDERATION_AUTH_URL, content=make_oneline(saml2_fixtures.SP_SOAP_RESPONSE)) - self.requests.register_uri('POST', - self.IDENTITY_PROVIDER_URL, - content=saml2_fixtures.SAML2_ASSERTION) + self.requests.post(self.IDENTITY_PROVIDER_URL, + content=saml2_fixtures.SAML2_ASSERTION) - self.requests.register_uri( - 'POST', + self.requests.post( self.SHIB_CONSUMER_URL, json=saml2_fixtures.UNSCOPED_TOKEN, headers={'X-Subject-Token': saml2_fixtures.UNSCOPED_TOKEN_HEADER, @@ -476,8 +463,8 @@ class AuthenticateviaADFSTests(utils.TestCase): def test_get_adfs_security_token(self): """Test ADFSUnscopedToken._get_adfs_security_token().""" - self.requests.register_uri( - 'POST', self.IDENTITY_PROVIDER_URL, + self.requests.post( + self.IDENTITY_PROVIDER_URL, content=make_oneline(self.ADFS_SECURITY_TOKEN_RESPONSE), status_code=200) @@ -539,9 +526,9 @@ class AuthenticateviaADFSTests(utils.TestCase): An exceptions.AuthorizationFailure should be raised including error message from the XML message indicating where was the problem. """ - self.requests.register_uri( - 'POST', self.IDENTITY_PROVIDER_URL, - content=make_oneline(self.ADFS_FAULT), status_code=500) + self.requests.post(self.IDENTITY_PROVIDER_URL, + content=make_oneline(self.ADFS_FAULT), + status_code=500) self.adfsplugin._prepare_adfs_request() self.assertRaises(exceptions.AuthorizationFailure, @@ -558,10 +545,9 @@ class AuthenticateviaADFSTests(utils.TestCase): and correctly raise exceptions.InternalServerError once it cannot parse XML fault message """ - self.requests.register_uri( - 'POST', self.IDENTITY_PROVIDER_URL, - content=b'NOT XML', - status_code=500) + self.requests.post(self.IDENTITY_PROVIDER_URL, + content=b'NOT XML', + status_code=500) self.adfsplugin._prepare_adfs_request() self.assertRaises(exceptions.InternalServerError, self.adfsplugin._get_adfs_security_token, @@ -573,9 +559,9 @@ class AuthenticateviaADFSTests(utils.TestCase): """Test whether SP issues a cookie.""" cookie = uuid.uuid4().hex - self.requests.register_uri('POST', self.SP_ENDPOINT, - headers={"set-cookie": cookie}, - status_code=302) + self.requests.post(self.SP_ENDPOINT, + headers={"set-cookie": cookie}, + status_code=302) self.adfsplugin.adfs_token = self._build_adfs_request() self.adfsplugin._prepare_sp_request() @@ -584,8 +570,7 @@ class AuthenticateviaADFSTests(utils.TestCase): self.assertEqual(1, len(self.session.session.cookies)) def test_send_assertion_to_service_provider_bad_status(self): - self.requests.register_uri('POST', self.SP_ENDPOINT, - status_code=500) + self.requests.post(self.SP_ENDPOINT, status_code=500) self.adfsplugin.adfs_token = etree.XML( self.ADFS_SECURITY_TOKEN_RESPONSE) @@ -605,10 +590,9 @@ class AuthenticateviaADFSTests(utils.TestCase): self.session) def test_check_valid_token_when_authenticated(self): - self.requests.register_uri( - 'GET', self.FEDERATION_AUTH_URL, - json=saml2_fixtures.UNSCOPED_TOKEN, - headers=client_fixtures.AUTH_RESPONSE_HEADERS) + self.requests.get(self.FEDERATION_AUTH_URL, + json=saml2_fixtures.UNSCOPED_TOKEN, + headers=client_fixtures.AUTH_RESPONSE_HEADERS) self.session.session.cookies = [object()] self.adfsplugin._access_service_provider(self.session) @@ -621,18 +605,15 @@ class AuthenticateviaADFSTests(utils.TestCase): response.json()['token']) def test_end_to_end_workflow(self): - self.requests.register_uri( - 'POST', self.IDENTITY_PROVIDER_URL, - content=self.ADFS_SECURITY_TOKEN_RESPONSE, - status_code=200) - self.requests.register_uri( - 'POST', self.SP_ENDPOINT, - headers={"set-cookie": 'x'}, - status_code=302) - self.requests.register_uri( - 'GET', self.FEDERATION_AUTH_URL, - json=saml2_fixtures.UNSCOPED_TOKEN, - headers=client_fixtures.AUTH_RESPONSE_HEADERS) + self.requests.post(self.IDENTITY_PROVIDER_URL, + content=self.ADFS_SECURITY_TOKEN_RESPONSE, + status_code=200) + self.requests.post(self.SP_ENDPOINT, + headers={"set-cookie": 'x'}, + status_code=302) + self.requests.get(self.FEDERATION_AUTH_URL, + json=saml2_fixtures.UNSCOPED_TOKEN, + headers=client_fixtures.AUTH_RESPONSE_HEADERS) # NOTE(marek-denis): We need to mimic this until self.requests can # issue cookies properly. diff --git a/keystoneclient/tests/v3/test_client.py b/keystoneclient/tests/v3/test_client.py index bf45032..e1a8542 100644 --- a/keystoneclient/tests/v3/test_client.py +++ b/keystoneclient/tests/v3/test_client.py @@ -40,6 +40,10 @@ class KeystoneClientTest(utils.TestCase): 'c4da488862bd435c9e6c0275a0d0e49a') self.assertFalse(c.has_service_catalog()) + self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a', + c.get_user_id(session=None)) + self.assertIsNone(c.get_project_id(session=None)) + def test_domain_scoped_init(self): self.stub_auth(json=client_fixtures.domain_scoped_token()) @@ -70,6 +74,10 @@ class KeystoneClientTest(utils.TestCase): 'c4da488862bd435c9e6c0275a0d0e49a') self.assertEqual(c.auth_tenant_id, '225da22d3ce34b15877ea70b2a575f58') + self.assertEqual('c4da488862bd435c9e6c0275a0d0e49a', + c.get_user_id(session=None)) + self.assertEqual('225da22d3ce34b15877ea70b2a575f58', + c.get_project_id(session=None)) def test_auth_ref_load(self): self.stub_auth(json=client_fixtures.project_scoped_token()) diff --git a/keystoneclient/tests/v3/test_discover.py b/keystoneclient/tests/v3/test_discover.py index 08e5358..4a1dee3 100644 --- a/keystoneclient/tests/v3/test_discover.py +++ b/keystoneclient/tests/v3/test_discover.py @@ -61,9 +61,9 @@ class DiscoverKeystoneTests(utils.UnauthenticatedTestCase): } def test_get_version_local(self): - self.requests.register_uri('GET', "http://localhost:35357/", - status_code=300, - json=self.TEST_RESPONSE_DICT) + self.requests.get("http://localhost:35357/", + status_code=300, + json=self.TEST_RESPONSE_DICT) cs = client.Client() versions = cs.discover() diff --git a/keystoneclient/tests/v3/test_domains.py b/keystoneclient/tests/v3/test_domains.py index e86971a..101d3f9 100644 --- a/keystoneclient/tests/v3/test_domains.py +++ b/keystoneclient/tests/v3/test_domains.py @@ -41,3 +41,8 @@ class DomainTests(utils.TestCase, utils.CrudTests): expected_query = {'enabled': '0'} super(DomainTests, self).test_list(expected_query=expected_query, enabled=False) + + def test_update_enabled_defaults_to_none(self): + req_ref = self.new_ref() + del req_ref['enabled'] + super(DomainTests, self).test_update(req_ref=req_ref) diff --git a/keystoneclient/tests/v3/test_federation.py b/keystoneclient/tests/v3/test_federation.py index 503da47..eed6680 100644 --- a/keystoneclient/tests/v3/test_federation.py +++ b/keystoneclient/tests/v3/test_federation.py @@ -349,8 +349,7 @@ class FederationProjectTests(utils.TestCase): projects_json = { self.collection_key: [self.new_ref(), self.new_ref()] } - self.requests.register_uri('GET', self.URL, - json=projects_json, status_code=200) + self.requests.get(self.URL, json=projects_json) returned_list = self.manager.list() self.assertEqual(len(projects_ref), len(returned_list)) @@ -381,8 +380,7 @@ class FederationDomainTests(utils.TestCase): domains_json = { self.collection_key: domains_ref } - self.requests.register_uri('GET', self.URL, - json=domains_json, status_code=200) + self.requests.get(self.URL, json=domains_json) returned_list = self.manager.list() self.assertEqual(len(domains_ref), len(returned_list)) for domain in returned_list: diff --git a/keystoneclient/tests/v3/test_regions.py b/keystoneclient/tests/v3/test_regions.py index c539aa7..3574ece 100644 --- a/keystoneclient/tests/v3/test_regions.py +++ b/keystoneclient/tests/v3/test_regions.py @@ -31,3 +31,8 @@ class RegionTests(utils.TestCase, utils.CrudTests): kwargs.setdefault('enabled', True) kwargs.setdefault('id', uuid.uuid4().hex) return kwargs + + def test_update_enabled_defaults_to_none(self): + req_ref = self.new_ref() + del req_ref['enabled'] + super(RegionTests, self).test_update(req_ref=req_ref) diff --git a/keystoneclient/tests/v3/test_services.py b/keystoneclient/tests/v3/test_services.py index d271afe..5367241 100644 --- a/keystoneclient/tests/v3/test_services.py +++ b/keystoneclient/tests/v3/test_services.py @@ -30,3 +30,15 @@ class ServiceTests(utils.TestCase, utils.CrudTests): kwargs.setdefault('type', uuid.uuid4().hex) kwargs.setdefault('enabled', True) return kwargs + + def test_list_filter_name(self): + filter_name = uuid.uuid4().hex + expected_query = {'name': filter_name} + super(ServiceTests, self).test_list(expected_query=expected_query, + name=filter_name) + + def test_list_filter_type(self): + filter_type = uuid.uuid4().hex + expected_query = {'type': filter_type} + super(ServiceTests, self).test_list(expected_query=expected_query, + type=filter_type) diff --git a/keystoneclient/tests/v3/utils.py b/keystoneclient/tests/v3/utils.py index 0929403..d4c201b 100644 --- a/keystoneclient/tests/v3/utils.py +++ b/keystoneclient/tests/v3/utils.py @@ -244,27 +244,22 @@ class CrudTests(object): ref_list = ref_list or [self.new_ref(), self.new_ref()] expected_path = self._get_expected_path(expected_path) - self.requests.register_uri('GET', - urlparse.urljoin(self.TEST_URL, - expected_path), - json=self.encode(ref_list)) + self.requests.get(urlparse.urljoin(self.TEST_URL, expected_path), + json=self.encode(ref_list)) returned_list = self.manager.list(**filter_kwargs) self.assertEqual(len(ref_list), len(returned_list)) [self.assertIsInstance(r, self.model) for r in returned_list] - # register_uri doesn't match the querystring component, so we have to - # explicitly test the querystring component passed by the manager - parts = urlparse.urlparse(self.requests.last_request.url) - qs_args = urlparse.parse_qs(parts.query) + qs_args = self.requests.last_request.qs qs_args_expected = expected_query or filter_kwargs for key, value in six.iteritems(qs_args_expected): self.assertIn(key, qs_args) - # The httppretty.querystring value is a list - # Note we convert the value to a string, as the query string - # is always a string and the filter_kwargs may contain non-string - # values, for example a boolean, causing the comaprison to fail. - self.assertIn(str(value), qs_args[key]) + # The querystring value is a list. Note we convert the value to a + # string and lower, as the query string is always a string and the + # filter_kwargs may contain non-string values, for example a + # boolean, causing the comaprison to fail. + self.assertIn(str(value).lower(), qs_args[key]) # Also check that no query string args exist which are not expected for key in qs_args: @@ -275,10 +270,8 @@ class CrudTests(object): filter_kwargs = {uuid.uuid4().hex: uuid.uuid4().hex} expected_path = self._get_expected_path() - self.requests.register_uri('GET', - urlparse.urljoin(self.TEST_URL, - expected_path), - json=self.encode(ref_list)) + self.requests.get(urlparse.urljoin(self.TEST_URL, expected_path), + json=self.encode(ref_list)) self.manager.list(**filter_kwargs) self.assertQueryStringContains(**filter_kwargs) diff --git a/keystoneclient/v2_0/certificates.py b/keystoneclient/v2_0/certificates.py new file mode 100644 index 0000000..b8d2573 --- /dev/null +++ b/keystoneclient/v2_0/certificates.py @@ -0,0 +1,42 @@ +# Copyright 2014 IBM Corp. +# 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 CertificatesManager(object): + """Manager for certificates.""" + + def __init__(self, client): + self._client = client + + def get_ca_certificate(self): + """Get CA certificate. + + :returns: PEM-formatted string. + :rtype: str + + """ + + resp, body = self._client.get('/certificates/ca', authenticated=False) + return resp.text + + def get_signing_certificate(self): + """Get signing certificate. + + :returns: PEM-formatted string. + :rtype: str + + """ + + resp, body = self._client.get('/certificates/signing', + authenticated=False) + return resp.text diff --git a/keystoneclient/v2_0/client.py b/keystoneclient/v2_0/client.py index f7bf153..bba556e 100644 --- a/keystoneclient/v2_0/client.py +++ b/keystoneclient/v2_0/client.py @@ -19,6 +19,7 @@ from keystoneclient.auth.identity import v2 as v2_auth from keystoneclient import exceptions from keystoneclient import httpclient from keystoneclient.i18n import _ +from keystoneclient.v2_0 import certificates from keystoneclient.v2_0 import ec2 from keystoneclient.v2_0 import endpoints from keystoneclient.v2_0 import extensions @@ -131,6 +132,7 @@ class Client(httpclient.HTTPClient): """Initialize a new client for the Keystone v2.0 API.""" super(Client, self).__init__(**kwargs) + self.certificates = certificates.CertificatesManager(self._adapter) self.endpoints = endpoints.EndpointManager(self._adapter) self.extensions = extensions.ExtensionManager(self._adapter) self.roles = roles.RoleManager(self._adapter) diff --git a/keystoneclient/v2_0/shell.py b/keystoneclient/v2_0/shell.py index a2cb1ab..9920555 100755 --- a/keystoneclient/v2_0/shell.py +++ b/keystoneclient/v2_0/shell.py @@ -248,11 +248,11 @@ def do_tenant_delete(kc, args): kc.tenants.delete(tenant) -@utils.arg('--name', metavar='<name>', required=True, - help='Name of new service (must be unique).') @utils.arg('--type', metavar='<type>', required=True, help='Service type (one of: identity, compute, network, ' 'image, object-store, or other service identifier string).') +@utils.arg('--name', metavar='<name>', + help='Name of new service (must be unique).') @utils.arg('--description', metavar='<service-description>', help='Description of service.') def do_service_create(kc, args): diff --git a/keystoneclient/v2_0/tokens.py b/keystoneclient/v2_0/tokens.py index fb48738..ed1c07e 100644 --- a/keystoneclient/v2_0/tokens.py +++ b/keystoneclient/v2_0/tokens.py @@ -72,3 +72,13 @@ class TokenManager(base.Manager): def endpoints(self, token): return self._get("/tokens/%s/endpoints" % base.getid(token), "token") + + def get_revoked(self): + """Returns the revoked tokens response. + + The response will be a dict containing 'signed' which is a CMS-encoded + document. + + """ + resp, body = self.client.get('/tokens/revoked') + return body diff --git a/keystoneclient/v3/domains.py b/keystoneclient/v3/domains.py index e0d082d..4439f41 100644 --- a/keystoneclient/v3/domains.py +++ b/keystoneclient/v3/domains.py @@ -60,7 +60,7 @@ class DomainManager(base.CrudManager): @utils.positional(enforcement=utils.positional.WARN) def update(self, domain, name=None, - description=None, enabled=True, **kwargs): + description=None, enabled=None, **kwargs): return super(DomainManager, self).update( domain_id=base.getid(domain), name=name, diff --git a/keystoneclient/v3/regions.py b/keystoneclient/v3/regions.py index de925e3..65cc6ab 100644 --- a/keystoneclient/v3/regions.py +++ b/keystoneclient/v3/regions.py @@ -65,7 +65,7 @@ class RegionManager(base.CrudManager): return super(RegionManager, self).list( **kwargs) - def update(self, region, description=None, enabled=True, + def update(self, region, description=None, enabled=None, parent_region=None, **kwargs): """Update a Catalog region. @@ -75,7 +75,7 @@ class RegionManager(base.CrudManager): pre-existing region in the backend. Allows for hierarchical region organization. :param enabled: determines whether the endpoint appears in the - catalog. Defaults to True + catalog. """ return super(RegionManager, self).update( diff --git a/keystoneclient/v3/services.py b/keystoneclient/v3/services.py index e0fd2c8..d6d4c80 100644 --- a/keystoneclient/v3/services.py +++ b/keystoneclient/v3/services.py @@ -51,6 +51,13 @@ class ServiceManager(base.CrudManager): service_id=base.getid(service)) @utils.positional(enforcement=utils.positional.WARN) + def list(self, name=None, type=None, **kwargs): + return super(ServiceManager, self).list( + name=name, + type=type, + **kwargs) + + @utils.positional(enforcement=utils.positional.WARN) def update(self, service, name=None, type=None, enabled=None, description=None, **kwargs): return super(ServiceManager, self).update( |