diff --git a/openstackclient/api/auth.py b/openstackclient/api/auth.py index c74e8005de..ded0e3699f 100644 --- a/openstackclient/api/auth.py +++ b/openstackclient/api/auth.py @@ -16,15 +16,12 @@ import argparse import logging -import stevedore - -from keystoneclient.auth import base +from keystoneauth1.loading import base from openstackclient.common import exceptions as exc from openstackclient.common import utils from openstackclient.i18n import _ - LOG = logging.getLogger(__name__) # Initialize the list of Authentication plugins early in order @@ -37,15 +34,10 @@ OPTIONS_LIST = {} def get_plugin_list(): """Gather plugin list and cache it""" - global PLUGIN_LIST if PLUGIN_LIST is None: - PLUGIN_LIST = stevedore.ExtensionManager( - base.PLUGIN_NAMESPACE, - invoke_on_load=False, - propagate_map_exceptions=True, - ) + PLUGIN_LIST = base.get_available_plugin_names() return PLUGIN_LIST @@ -55,8 +47,9 @@ def get_options_list(): global OPTIONS_LIST if not OPTIONS_LIST: - for plugin in get_plugin_list(): - for o in plugin.plugin.get_options(): + for plugin_name in get_plugin_list(): + plugin_options = base.get_plugin_options(plugin_name) + for o in plugin_options: os_name = o.dest.lower().replace('_', '-') os_env_name = 'OS_' + os_name.upper().replace('-', '_') OPTIONS_LIST.setdefault( @@ -66,7 +59,7 @@ def get_options_list(): # help texts if they vary from one auth plugin to another # also the text rendering is ugly in the CLI ... OPTIONS_LIST[os_name]['help'] += 'With %s: %s\n' % ( - plugin.name, + plugin_name, o.help, ) return OPTIONS_LIST @@ -83,7 +76,7 @@ def select_auth_plugin(options): if options.auth.get('url') and options.auth.get('token'): # service token authentication auth_plugin_name = 'token_endpoint' - elif options.auth_type in [plugin.name for plugin in PLUGIN_LIST]: + elif options.auth_type in PLUGIN_LIST: # A direct plugin name was given, use it auth_plugin_name = options.auth_type elif options.auth.get('username'): @@ -115,7 +108,7 @@ def build_auth_params(auth_plugin_name, cmd_options): auth_params = dict(cmd_options.auth) if auth_plugin_name: LOG.debug('auth_type: %s', auth_plugin_name) - auth_plugin_class = base.get_plugin_class(auth_plugin_name) + auth_plugin_loader = base.get_plugin_loader(auth_plugin_name) # grab tenant from project for v2.0 API compatibility if auth_plugin_name.startswith("v2"): if 'project_id' in auth_params: @@ -127,12 +120,12 @@ def build_auth_params(auth_plugin_name, cmd_options): else: LOG.debug('no auth_type') # delay the plugin choice, grab every option - auth_plugin_class = None + auth_plugin_loader = None plugin_options = set([o.replace('-', '_') for o in get_options_list()]) for option in plugin_options: LOG.debug('fetching option %s', option) auth_params[option] = getattr(cmd_options.auth, option, None) - return (auth_plugin_class, auth_params) + return (auth_plugin_loader, auth_params) def check_valid_auth_options(options, auth_plugin_name, required_scope=True): @@ -188,7 +181,7 @@ def build_auth_plugins_option_parser(parser): authentication plugin. """ - available_plugins = [plugin.name for plugin in get_plugin_list()] + available_plugins = list(get_plugin_list()) parser.add_argument( '--os-auth-type', metavar='', diff --git a/openstackclient/api/auth_plugin.py b/openstackclient/api/auth_plugin.py index cff0b75dc9..44d3b38ea4 100644 --- a/openstackclient/api/auth_plugin.py +++ b/openstackclient/api/auth_plugin.py @@ -18,13 +18,13 @@ import logging from oslo_config import cfg from six.moves.urllib import parse as urlparse -from keystoneclient.auth.identity.generic import password as ksc_password -from keystoneclient.auth import token_endpoint +from keystoneauth1.loading._plugins import admin_token as token_endpoint +from keystoneauth1.loading._plugins.identity import generic as ksa_password LOG = logging.getLogger(__name__) -class TokenEndpoint(token_endpoint.Token): +class TokenEndpoint(token_endpoint.AdminToken): """Auth plugin to handle traditional token/endpoint usage Implements the methods required to handle token authentication @@ -36,20 +36,15 @@ class TokenEndpoint(token_endpoint.Token): is for bootstrapping the Keystone database. """ - def __init__(self, url, token, **kwargs): + def load_from_options(self, url, token): """A plugin for static authentication with an existing token :param string url: Service endpoint :param string token: Existing token """ - super(TokenEndpoint, self).__init__(endpoint=url, - token=token) + return super(TokenEndpoint, self).load_from_options(endpoint=url, + token=token) - def get_auth_ref(self, session, **kwargs): - # Stub this method for compatibility - return None - - @classmethod def get_options(self): options = super(TokenEndpoint, self).get_options() @@ -65,7 +60,7 @@ class TokenEndpoint(token_endpoint.Token): return options -class OSCGenericPassword(ksc_password.Password): +class OSCGenericPassword(ksa_password.Password): """Auth plugin hack to work around broken Keystone configurations The default Keystone configuration uses http://localhost:xxxx in diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index 9c2b320c1d..e8ba7ec446 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -269,7 +269,7 @@ class ClientManager(object): endpoint = self.auth_ref.service_catalog.url_for( service_type=service_type, region_name=region_name, - endpoint_type=interface, + interface=interface, ) else: # Get the passed endpoint directly from the auth plugin diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index c8f48cb63f..33692a0d6c 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -16,6 +16,7 @@ import six from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.common import utils from openstackclient.i18n import _ @@ -41,13 +42,14 @@ class ListCatalog(command.Lister): def take_action(self, parsed_args): - # This is ugly because if auth hasn't happened yet we need - # to trigger it here. - sc = self.app.client_manager.session.auth.get_auth_ref( - self.app.client_manager.session, - ).service_catalog + # Trigger auth if it has not happened yet + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: + raise exceptions.AuthorizationFailure( + "Only an authorized user may issue a new token." + ) - data = sc.get_data() + data = auth_ref.service_catalog.catalog columns = ('Name', 'Type', 'Endpoints') return (columns, (utils.get_dict_properties( @@ -72,14 +74,15 @@ class ShowCatalog(command.ShowOne): def take_action(self, parsed_args): - # This is ugly because if auth hasn't happened yet we need - # to trigger it here. - sc = self.app.client_manager.session.auth.get_auth_ref( - self.app.client_manager.session, - ).service_catalog + # Trigger auth if it has not happened yet + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: + raise exceptions.AuthorizationFailure( + "Only an authorized user may issue a new token." + ) data = None - for service in sc.get_data(): + for service in auth_ref.service_catalog.catalog: if (service.get('name') == parsed_args.service or service.get('type') == parsed_args.service): data = service @@ -91,6 +94,6 @@ class ShowCatalog(command.ShowOne): if not data: self.app.log.error(_('service %s not found\n') % parsed_args.service) - return ([], []) + return ((), ()) return zip(*sorted(six.iteritems(data))) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 6b014d8651..0f8da99249 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -231,18 +231,19 @@ class ListUserRole(command.Lister): # Project and user are required, if not included in command args # default to the values used for authentication. For token-flow # authentication they must be included on the command line. + if (not parsed_args.project and + self.app.client_manager.auth_ref.project_id): + parsed_args.project = auth_ref.project_id if not parsed_args.project: - if self.app.client_manager.auth_ref: - parsed_args.project = auth_ref.project_id - else: - msg = _("Project must be specified") - raise exceptions.CommandError(msg) + msg = _("Project must be specified") + raise exceptions.CommandError(msg) + + if (not parsed_args.user and + self.app.client_manager.auth_ref.user_id): + parsed_args.user = auth_ref.user_id if not parsed_args.user: - if self.app.client_manager.auth_ref: - parsed_args.user = auth_ref.user_id - else: - msg = _("User must be specified") - raise exceptions.CommandError(msg) + msg = _("User must be specified") + raise exceptions.CommandError(msg) project = utils.find_resource( identity_client.tenants, diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index f435d7ce98..d708749d30 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -18,6 +18,7 @@ import six from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.i18n import _ @@ -32,11 +33,21 @@ class IssueToken(command.ShowOne): return parser def take_action(self, parsed_args): + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: + raise exceptions.AuthorizationFailure( + "Only an authorized user may issue a new token.") - token = self.app.client_manager.auth_ref.service_catalog.get_token() - if 'tenant_id' in token: - token['project_id'] = token.pop('tenant_id') - return zip(*sorted(six.iteritems(token))) + data = {} + if auth_ref.auth_token: + data['id'] = auth_ref.auth_token + if auth_ref.expires: + data['expires'] = auth_ref.expires + if auth_ref.project_id: + data['project_id'] = auth_ref.project_id + if auth_ref.user_id: + data['user_id'] = auth_ref.user_id + return zip(*sorted(six.iteritems(data))) class RevokeToken(command.Command): diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index 4c794692d4..c2b4359d28 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -16,6 +16,7 @@ import six from openstackclient.common import command +from openstackclient.common import exceptions from openstackclient.common import utils from openstackclient.i18n import _ @@ -36,13 +37,14 @@ class ListCatalog(command.Lister): def take_action(self, parsed_args): - # This is ugly because if auth hasn't happened yet we need - # to trigger it here. - sc = self.app.client_manager.session.auth.get_auth_ref( - self.app.client_manager.session, - ).service_catalog + # Trigger auth if it has not happened yet + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: + raise exceptions.AuthorizationFailure( + "Only an authorized user may issue a new token." + ) - data = sc.get_data() + data = auth_ref.service_catalog.catalog columns = ('Name', 'Type', 'Endpoints') return (columns, (utils.get_dict_properties( @@ -67,14 +69,15 @@ class ShowCatalog(command.ShowOne): def take_action(self, parsed_args): - # This is ugly because if auth hasn't happened yet we need - # to trigger it here. - sc = self.app.client_manager.session.auth.get_auth_ref( - self.app.client_manager.session, - ).service_catalog + # Trigger auth if it has not happened yet + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: + raise exceptions.AuthorizationFailure( + "Only an authorized user may issue a new token." + ) data = None - for service in sc.get_data(): + for service in auth_ref.service_catalog.catalog: if (service.get('name') == parsed_args.service or service.get('type') == parsed_args.service): data = dict(service) @@ -86,6 +89,6 @@ class ShowCatalog(command.ShowOne): if not data: self.app.log.error(_('service %s not found\n') % parsed_args.service) - return ([], []) + return ((), ()) return zip(*sorted(six.iteritems(data))) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 56a7497cfc..cc3993631b 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -174,13 +174,23 @@ class IssueToken(command.ShowOne): return parser def take_action(self, parsed_args): - if not self.app.client_manager.auth_ref: + auth_ref = self.app.client_manager.auth_ref + if not auth_ref: raise exceptions.AuthorizationFailure( _("Only an authorized user may issue a new token.")) - token = self.app.client_manager.auth_ref.service_catalog.get_token() - if 'tenant_id' in token: - token['project_id'] = token.pop('tenant_id') - return zip(*sorted(six.iteritems(token))) + + data = {} + if auth_ref.auth_token: + data['id'] = auth_ref.auth_token + if auth_ref.expires: + data['expires'] = auth_ref.expires + if auth_ref.project_id: + data['project_id'] = auth_ref.project_id + if auth_ref.user_id: + data['user_id'] = auth_ref.user_id + if auth_ref.domain_id: + data['domain_id'] = auth_ref.domain_id + return zip(*sorted(six.iteritems(data))) class RevokeToken(command.Command): diff --git a/openstackclient/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py index fa6c3fcc25..33485b00a9 100644 --- a/openstackclient/tests/common/test_clientmanager.py +++ b/openstackclient/tests/common/test_clientmanager.py @@ -17,11 +17,11 @@ import json as jsonutils import mock from requests_mock.contrib import fixture -from keystoneclient.auth.identity import v2 as auth_v2 -from keystoneclient import service_catalog +from keystoneauth1.access import service_catalog +from keystoneauth1.identity import v2 as auth_v2 +from keystoneauth1 import token_endpoint from openstackclient.api import auth -from openstackclient.api import auth_plugin from openstackclient.common import clientmanager from openstackclient.common import exceptions as exc from openstackclient.tests import fakes @@ -29,7 +29,6 @@ from openstackclient.tests import utils API_VERSION = {"identity": "2.0"} - AUTH_REF = {'version': 'v2.0'} AUTH_REF.update(fakes.TEST_RESPONSE_DICT['access']) SERVICE_CATALOG = service_catalog.ServiceCatalogV2(AUTH_REF) @@ -126,7 +125,7 @@ class TestClientManager(utils.TestCase): ) self.assertIsInstance( client_manager.auth, - auth_plugin.TokenEndpoint, + token_endpoint.Token, ) self.assertFalse(client_manager._insecure) self.assertTrue(client_manager._verify) @@ -205,11 +204,14 @@ class TestClientManager(utils.TestCase): ) self.assertTrue(client_manager._insecure) self.assertFalse(client_manager._verify) - # These need to stick around until the old-style clients are gone self.assertEqual( - AUTH_REF, - client_manager.auth_ref, + AUTH_REF.pop('version'), + client_manager.auth_ref.version, + ) + self.assertEqual( + fakes.to_unicode_dict(AUTH_REF), + client_manager.auth_ref._data['access'], ) self.assertEqual( dir(SERVICE_CATALOG), @@ -296,9 +298,10 @@ class TestClientManager(utils.TestCase): def _select_auth_plugin(self, auth_params, api_version, auth_plugin_name): auth_params['auth_type'] = auth_plugin_name auth_params['identity_api_version'] = api_version + client_manager = clientmanager.ClientManager( cli_options=FakeOptions(**auth_params), - api_version=API_VERSION, + api_version={"identity": api_version}, verify=True ) client_manager.setup_auth() diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py index ad4705a4de..d0cab0191a 100644 --- a/openstackclient/tests/fakes.py +++ b/openstackclient/tests/fakes.py @@ -50,6 +50,21 @@ TEST_RESPONSE_DICT_V3.set_project_scope() TEST_VERSIONS = fixture.DiscoveryList(href=AUTH_URL) +def to_unicode_dict(catalog_dict): + """Converts dict to unicode dict + + """ + if isinstance(catalog_dict, dict): + return {to_unicode_dict(key): to_unicode_dict(value) + for key, value in catalog_dict.items()} + elif isinstance(catalog_dict, list): + return [to_unicode_dict(element) for element in catalog_dict] + elif isinstance(catalog_dict, str): + return catalog_dict + u"" + else: + return catalog_dict + + class FakeStdout(object): def __init__(self): diff --git a/openstackclient/tests/identity/v2_0/fakes.py b/openstackclient/tests/identity/v2_0/fakes.py index b80938729a..c613ad82a3 100644 --- a/openstackclient/tests/identity/v2_0/fakes.py +++ b/openstackclient/tests/identity/v2_0/fakes.py @@ -17,6 +17,9 @@ import copy import mock import uuid +from keystoneauth1 import access +from keystoneauth1 import fixture + from openstackclient.tests import fakes from openstackclient.tests import utils @@ -109,6 +112,43 @@ ENDPOINT = { } +def fake_auth_ref(fake_token, fake_service=None): + """Create an auth_ref using keystoneauth's fixtures""" + token_copy = copy.deepcopy(fake_token) + token_copy['token_id'] = token_copy.pop('id') + token = fixture.V2Token(**token_copy) + # An auth_ref is actually an access info object + auth_ref = access.create(body=token) + + # Create a service catalog + if fake_service: + service = token.add_service( + fake_service['type'], + fake_service['name'], + ) + # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure + service['id'] = fake_service['id'] + for e in fake_service['endpoints']: + # KSA's _Service fixture copies publicURL to internalURL and + # adminURL if they do not exist. Soooo helpful... + internal = e.get('internalURL', None) + admin = e.get('adminURL', None) + region = e.get('region_id') or e.get('region', '') + endpoint = service.add_endpoint( + public=e['publicURL'], + internal=internal, + admin=admin, + region=region, + ) + # ...so undo that helpfulness + if not internal: + endpoint['internalURL'] = None + if not admin: + endpoint['adminURL'] = None + + return auth_ref + + class FakeIdentityv2Client(object): def __init__(self, **kwargs): diff --git a/openstackclient/tests/identity/v2_0/test_catalog.py b/openstackclient/tests/identity/v2_0/test_catalog.py index d9ae6a80b6..2fdbafbea2 100644 --- a/openstackclient/tests/identity/v2_0/test_catalog.py +++ b/openstackclient/tests/identity/v2_0/test_catalog.py @@ -14,6 +14,7 @@ import mock from openstackclient.identity.v2_0 import catalog +from openstackclient.tests.identity.v2_0 import fakes as identity_fakes from openstackclient.tests import utils @@ -49,7 +50,7 @@ class TestCatalog(utils.TestCommand): super(TestCatalog, self).setUp() self.sc_mock = mock.MagicMock() - self.sc_mock.service_catalog.get_data.return_value = [ + self.sc_mock.service_catalog.catalog.return_value = [ self.fake_service, ] @@ -74,6 +75,13 @@ class TestCatalogList(TestCatalog): self.cmd = catalog.ListCatalog(self.app, None) def test_catalog_list(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -82,7 +90,6 @@ class TestCatalogList(TestCatalog): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.service_catalog.get_data.assert_called_with() self.assertEqual(self.columns, columns) datalist = (( @@ -117,9 +124,12 @@ class TestCatalogList(TestCatalog): }, ], } - self.sc_mock.service_catalog.get_data.return_value = [ - fake_service, - ] + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock arglist = [] verifylist = [] @@ -129,7 +139,6 @@ class TestCatalogList(TestCatalog): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.service_catalog.get_data.assert_called_with() self.assertEqual(self.columns, columns) datalist = (( @@ -151,6 +160,13 @@ class TestCatalogShow(TestCatalog): self.cmd = catalog.ShowCatalog(self.app, None) def test_catalog_show(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [ 'compute', ] @@ -163,7 +179,6 @@ class TestCatalogShow(TestCatalog): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.service_catalog.get_data.assert_called_with() collist = ('endpoints', 'id', 'name', 'type') self.assertEqual(collist, columns) diff --git a/openstackclient/tests/identity/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py index 3c4b79a4a3..486a4a2ab4 100644 --- a/openstackclient/tests/identity/v2_0/test_role.py +++ b/openstackclient/tests/identity/v2_0/test_role.py @@ -26,6 +26,13 @@ from openstackclient.tests.identity.v2_0 import fakes as identity_fakes class TestRole(identity_fakes.TestIdentityv2): + fake_service = copy.deepcopy(identity_fakes.SERVICE) + fake_service['endpoints'] = [ + { + 'publicURL': identity_fakes.ENDPOINT['publicurl'], + }, + ] + def setUp(self): super(TestRole, self).setUp() @@ -41,6 +48,13 @@ class TestRole(identity_fakes.TestIdentityv2): self.roles_mock = self.app.client_manager.identity.roles self.roles_mock.reset_mock() + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + class TestRoleAdd(TestRole): @@ -320,7 +334,14 @@ class TestUserRoleList(TestRole): # Get the command object to test self.cmd = role.ListUserRole(self.app, None) - def test_user_role_list_no_options(self): + def test_user_role_list_no_options_unscoped_token(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -332,11 +353,7 @@ class TestUserRoleList(TestRole): parsed_args, ) - def test_user_role_list_no_options_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.MagicMock() - auth_ref.project_id.return_value = identity_fakes.project_id - auth_ref.user_id.return_value = identity_fakes.user_id - + def test_user_role_list_no_options_scoped_token(self): arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -361,7 +378,14 @@ class TestUserRoleList(TestRole): ), ) self.assertEqual(datalist, tuple(data)) - def test_user_role_list_project(self): + def test_user_role_list_project_unscoped_token(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + self.projects_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.PROJECT_2), @@ -375,18 +399,26 @@ class TestUserRoleList(TestRole): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - # This argument combination should raise a CommandError - self.assertRaises( - exceptions.CommandError, - self.cmd.take_action, - parsed_args, + # In base command class Lister in cliff, abstract method take_action() + # returns a tuple containing the column names and an iterable + # containing the data to be listed. + columns, data = self.cmd.take_action(parsed_args) + + self.roles_mock.roles_for_user.assert_called_with( + identity_fakes.user_id, + identity_fakes.PROJECT_2['id'], ) - def test_user_role_list_project_def_creds(self): - auth_ref = self.app.client_manager.auth_ref = mock.MagicMock() - auth_ref.project_id.return_value = identity_fakes.project_id - auth_ref.user_id.return_value = identity_fakes.user_id + self.assertEqual(columns, columns) + datalist = (( + identity_fakes.role_id, + identity_fakes.role_name, + identity_fakes.PROJECT_2['name'], + identity_fakes.user_name, + ), ) + self.assertEqual(datalist, tuple(data)) + def test_user_role_list_project_scoped_token(self): self.projects_mock.get.return_value = fakes.FakeResource( None, copy.deepcopy(identity_fakes.PROJECT_2), diff --git a/openstackclient/tests/identity/v2_0/test_token.py b/openstackclient/tests/identity/v2_0/test_token.py index 613139dd3a..96f08e8708 100644 --- a/openstackclient/tests/identity/v2_0/test_token.py +++ b/openstackclient/tests/identity/v2_0/test_token.py @@ -24,10 +24,9 @@ class TestToken(identity_fakes.TestIdentityv2): def setUp(self): super(TestToken, self).setUp() - # Get a shortcut to the Service Catalog Mock - self.sc_mock = mock.Mock() - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = self.sc_mock + # Get a shortcut to the Auth Ref Mock + self.ar_mock = mock.PropertyMock() + type(self.app.client_manager).auth_ref = self.ar_mock class TestTokenIssue(TestToken): @@ -35,10 +34,15 @@ class TestTokenIssue(TestToken): def setUp(self): super(TestTokenIssue, self).setUp() - self.sc_mock.get_token.return_value = identity_fakes.TOKEN self.cmd = token.IssueToken(self.app, None) def test_token_issue(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -48,12 +52,10 @@ class TestTokenIssue(TestToken): # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.get_token.assert_called_with() - collist = ('expires', 'id', 'project_id', 'user_id') self.assertEqual(collist, columns) datalist = ( - identity_fakes.token_expires, + auth_ref.expires, identity_fakes.token_id, identity_fakes.project_id, identity_fakes.user_id, @@ -61,8 +63,11 @@ class TestTokenIssue(TestToken): self.assertEqual(datalist, data) def test_token_issue_with_unscoped_token(self): - # make sure we return an unscoped token - self.sc_mock.get_token.return_value = identity_fakes.UNSCOPED_TOKEN + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock arglist = [] verifylist = [] @@ -71,12 +76,14 @@ class TestTokenIssue(TestToken): # DisplayCommandBase.take_action() returns two tuples columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.get_token.assert_called_with() - - collist = ('expires', 'id', 'user_id') + collist = ( + 'expires', + 'id', + 'user_id', + ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.token_expires, + auth_ref.expires, identity_fakes.token_id, identity_fakes.user_id, ) diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py index 1422166a90..cd1b4bd77d 100644 --- a/openstackclient/tests/identity/v3/fakes.py +++ b/openstackclient/tests/identity/v3/fakes.py @@ -13,8 +13,12 @@ # under the License. # +import copy import mock +from keystoneauth1 import access +from keystoneauth1 import fixture + from openstackclient.tests import fakes from openstackclient.tests import utils @@ -419,6 +423,36 @@ OAUTH_VERIFIER = { } +def fake_auth_ref(fake_token, fake_service=None): + """Create an auth_ref using keystoneauth's fixtures""" + token_copy = copy.deepcopy(fake_token) + token_id = token_copy.pop('id') + token = fixture.V3Token(**token_copy) + # An auth_ref is actually an access info object + auth_ref = access.create( + body=token, + auth_token=token_id, + ) + + # Create a service catalog + if fake_service: + service = token.add_service( + fake_service['type'], + fake_service['name'], + ) + # TODO(dtroyer): Add an 'id' element to KSA's _Service fixure + service['id'] = fake_service['id'] + for e in fake_service['endpoints']: + region = e.get('region_id') or e.get('region', '') + service.add_endpoint( + e['interface'], + e['url'], + region=region, + ) + + return auth_ref + + class FakeAuth(object): def __init__(self, auth_method_class=None): diff --git a/openstackclient/tests/identity/v3/test_catalog.py b/openstackclient/tests/identity/v3/test_catalog.py index 1b8fa08586..e3c5ed3d88 100644 --- a/openstackclient/tests/identity/v3/test_catalog.py +++ b/openstackclient/tests/identity/v3/test_catalog.py @@ -14,6 +14,7 @@ import mock from openstackclient.identity.v3 import catalog +from openstackclient.tests.identity.v3 import fakes as identity_fakes from openstackclient.tests import utils @@ -50,7 +51,7 @@ class TestCatalog(utils.TestCommand): super(TestCatalog, self).setUp() self.sc_mock = mock.MagicMock() - self.sc_mock.service_catalog.get_data.return_value = [ + self.sc_mock.service_catalog.catalog.return_value = [ self.fake_service, ] @@ -69,6 +70,13 @@ class TestCatalogList(TestCatalog): self.cmd = catalog.ListCatalog(self.app, None) def test_catalog_list(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -77,7 +85,6 @@ class TestCatalogList(TestCatalog): # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.service_catalog.get_data.assert_called_with() collist = ('Name', 'Type', 'Endpoints') self.assertEqual(collist, columns) @@ -101,6 +108,13 @@ class TestCatalogShow(TestCatalog): self.cmd = catalog.ShowCatalog(self.app, None) def test_catalog_show(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + fake_service=self.fake_service, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [ 'compute', ] @@ -113,7 +127,6 @@ class TestCatalogShow(TestCatalog): # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.service_catalog.get_data.assert_called_with() collist = ('endpoints', 'id', 'name', 'type') self.assertEqual(collist, columns) diff --git a/openstackclient/tests/identity/v3/test_token.py b/openstackclient/tests/identity/v3/test_token.py index b68bc242ec..9728c6e134 100644 --- a/openstackclient/tests/identity/v3/test_token.py +++ b/openstackclient/tests/identity/v3/test_token.py @@ -24,10 +24,9 @@ class TestToken(identity_fakes.TestIdentityv3): def setUp(self): super(TestToken, self).setUp() - # Get a shortcut to the Service Catalog Mock - self.sc_mock = mock.Mock() - self.app.client_manager.auth_ref = mock.Mock() - self.app.client_manager.auth_ref.service_catalog = self.sc_mock + # Get a shortcut to the Auth Ref Mock + self.ar_mock = mock.PropertyMock() + type(self.app.client_manager).auth_ref = self.ar_mock class TestTokenIssue(TestToken): @@ -38,23 +37,25 @@ class TestTokenIssue(TestToken): self.cmd = token.IssueToken(self.app, None) def test_token_issue_with_project_id(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_PROJECT_ID, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sc_mock.get_token.return_value = \ - identity_fakes.TOKEN_WITH_PROJECT_ID # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.get_token.assert_called_with() - collist = ('expires', 'id', 'project_id', 'user_id') self.assertEqual(collist, columns) datalist = ( - identity_fakes.token_expires, + auth_ref.expires, identity_fakes.token_id, identity_fakes.project_id, identity_fakes.user_id, @@ -62,45 +63,53 @@ class TestTokenIssue(TestToken): self.assertEqual(datalist, data) def test_token_issue_with_domain_id(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.TOKEN_WITH_DOMAIN_ID, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sc_mock.get_token.return_value = \ - identity_fakes.TOKEN_WITH_DOMAIN_ID # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.get_token.assert_called_with() - collist = ('domain_id', 'expires', 'id', 'user_id') self.assertEqual(collist, columns) datalist = ( identity_fakes.domain_id, - identity_fakes.token_expires, + auth_ref.expires, identity_fakes.token_id, identity_fakes.user_id, ) self.assertEqual(datalist, data) def test_token_issue_with_unscoped(self): + auth_ref = identity_fakes.fake_auth_ref( + identity_fakes.UNSCOPED_TOKEN, + ) + self.ar_mock = mock.PropertyMock(return_value=auth_ref) + type(self.app.client_manager).auth_ref = self.ar_mock + arglist = [] verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - self.sc_mock.get_token.return_value = \ - identity_fakes.UNSCOPED_TOKEN # DisplayCommandBase.take_action() returns two tuples columns, data = self.cmd.take_action(parsed_args) - self.sc_mock.get_token.assert_called_with() - - collist = ('expires', 'id', 'user_id') + collist = ( + 'expires', + 'id', + 'user_id', + ) self.assertEqual(collist, columns) datalist = ( - identity_fakes.token_expires, + auth_ref.expires, identity_fakes.token_id, identity_fakes.user_id, ) diff --git a/setup.cfg b/setup.cfg index 2bff76b1c5..db5fa0966a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,7 @@ packages = console_scripts = openstack = openstackclient.shell:main -keystoneclient.auth.plugin = +keystoneauth1.plugin = token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint osc_password = openstackclient.api.auth_plugin:OSCGenericPassword