From 6ae0d2e8a54fd5139e63a990ab4bdce634e73c5e Mon Sep 17 00:00:00 2001
From: Navid Pustchi <npustchi@gmail.com>
Date: Thu, 4 Feb 2016 16:45:38 +0000
Subject: [PATCH] Moving authentication from keystoneclient to keystoneauth

Currently OpenStackClient uses keystoneclient for authentication.
This change will update OpenStackClient to use keystoneauth for
authentication.

All dependant test have been updated.

Updating how auth_ref is set in the tests to use KSA fixtures had
some racy side-effects.  The user_role_list tests failed when they
picked up an auth_ref that was a fixture.  This exposed a weakness
in ListUserRole that needed to be fixed at the same time re
handling of unscoped tokens and options.

Change-Id: I4ddb2dbbb3bf2ab37494468eaf65cef9213a6e00
Closes-Bug: 1533369
---
 openstackclient/api/auth.py                   | 29 ++++-----
 openstackclient/api/auth_plugin.py            | 19 ++----
 openstackclient/common/clientmanager.py       |  2 +-
 openstackclient/identity/v2_0/catalog.py      | 29 +++++----
 openstackclient/identity/v2_0/role.py         | 21 +++---
 openstackclient/identity/v2_0/token.py        | 19 ++++--
 openstackclient/identity/v3/catalog.py        | 29 +++++----
 openstackclient/identity/v3/token.py          | 20 ++++--
 .../tests/common/test_clientmanager.py        | 21 +++---
 openstackclient/tests/fakes.py                | 15 +++++
 openstackclient/tests/identity/v2_0/fakes.py  | 40 ++++++++++++
 .../tests/identity/v2_0/test_catalog.py       | 29 +++++++--
 .../tests/identity/v2_0/test_role.py          | 64 ++++++++++++++-----
 .../tests/identity/v2_0/test_token.py         | 35 ++++++----
 openstackclient/tests/identity/v3/fakes.py    | 34 ++++++++++
 .../tests/identity/v3/test_catalog.py         | 19 +++++-
 .../tests/identity/v3/test_token.py           | 49 ++++++++------
 setup.cfg                                     |  2 +-
 18 files changed, 330 insertions(+), 146 deletions(-)

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='<auth-type>',
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', '<none>')
+            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', '<none>')
+            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