Move OSC auth plugins so they can be found

The plugin detection at the top of openstackclient.api.auth did not
detect the plugins at the bottom of that file because, surprise,
they had not been declared yet so the entry points were ignored.

Move both plugin subclasses into openstackclient.api.auth_plugin.

Fix a problem with the password callback that was otherwise made
worse with this change.

Closes-Bug: 1428912
Change-Id: Idc3b72534071e0013c8922884a8bc14137509a0f
This commit is contained in:
Dean Troyer 2015-03-03 20:29:38 -06:00 committed by Steve Martinelli
parent dc00d7c758
commit 749920bc65
7 changed files with 123 additions and 113 deletions

View File

@ -84,12 +84,6 @@ def base_parser(parser):
"""
# Global arguments
parser.add_argument(
'--os-url',
metavar='<url>',
default=env('OS_URL'),
help='Defaults to env[OS_URL]',
)
parser.add_argument(
'--os-region-name',
metavar='<auth-region-name>',

View File

@ -16,13 +16,9 @@
import argparse
import logging
from six.moves.urllib import parse as urlparse
import stevedore
from oslo_config import cfg
from keystoneclient.auth import base
from keystoneclient.auth.identity.generic import password as ksc_password
from openstackclient.common import exceptions as exc
from openstackclient.common import utils
@ -201,96 +197,3 @@ def build_auth_plugins_option_parser(parser):
help=argparse.SUPPRESS,
)
return parser
class TokenEndpoint(base.BaseAuthPlugin):
"""Auth plugin to handle traditional token/endpoint usage
Implements the methods required to handle token authentication
with a user-specified token and service endpoint; no Identity calls
are made for re-scoping, service catalog lookups or the like.
The purpose of this plugin is to get rid of the special-case paths
in the code to handle this authentication format. Its primary use
is for bootstrapping the Keystone database.
"""
def __init__(self, url, token, **kwargs):
"""A plugin for static authentication with an existing token
:param string url: Service endpoint
:param string token: Existing token
"""
super(TokenEndpoint, self).__init__()
self.endpoint = url
self.token = token
def get_endpoint(self, session, **kwargs):
"""Return the supplied endpoint"""
return self.endpoint
def get_token(self, session):
"""Return the supplied token"""
return self.token
def get_auth_ref(self, session, **kwargs):
"""Stub this method for compatibility"""
return None
# Override this because it needs to be a class method...
@classmethod
def get_options(self):
options = super(TokenEndpoint, self).get_options()
options.extend([
# Maintain name 'url' for compatibility
cfg.StrOpt('url',
help='Specific service endpoint to use'),
cfg.StrOpt('token',
secret=True,
help='Authentication token to use'),
])
return options
class OSCGenericPassword(ksc_password.Password):
"""Auth plugin hack to work around broken Keystone configurations
The default Keystone configuration uses http://localhost:xxxx in
admin_endpoint and public_endpoint and are returned in the links.href
attribute by the version routes. Deployments that do not set these
are unusable with newer keystoneclient version discovery.
"""
def create_plugin(self, session, version, url, raw_status=None):
"""Handle default Keystone endpoint configuration
Build the actual API endpoint from the scheme, host and port of the
original auth URL and the rest from the returned version URL.
"""
ver_u = urlparse.urlparse(url)
# Only hack this if it is the default setting
if ver_u.netloc.startswith('localhost'):
auth_u = urlparse.urlparse(self.auth_url)
# from original auth_url: scheme, netloc
# from api_url: path, query (basically, the rest)
url = urlparse.urlunparse((
auth_u.scheme,
auth_u.netloc,
ver_u.path,
ver_u.params,
ver_u.query,
ver_u.fragment,
))
LOG.debug('Version URL updated: %s' % url)
return super(OSCGenericPassword, self).create_plugin(
session=session,
version=version,
url=url,
raw_status=raw_status,
)

View File

@ -0,0 +1,118 @@
# 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.
#
"""Authentication Plugin Library"""
import logging
from oslo_config import cfg
from six.moves.urllib import parse as urlparse
from keystoneclient.auth import base
from keystoneclient.auth.identity.generic import password as ksc_password
LOG = logging.getLogger(__name__)
class TokenEndpoint(base.BaseAuthPlugin):
"""Auth plugin to handle traditional token/endpoint usage
Implements the methods required to handle token authentication
with a user-specified token and service endpoint; no Identity calls
are made for re-scoping, service catalog lookups or the like.
The purpose of this plugin is to get rid of the special-case paths
in the code to handle this authentication format. Its primary use
is for bootstrapping the Keystone database.
"""
def __init__(self, url, token, **kwargs):
"""A plugin for static authentication with an existing token
:param string url: Service endpoint
:param string token: Existing token
"""
super(TokenEndpoint, self).__init__()
self.endpoint = url
self.token = token
def get_endpoint(self, session, **kwargs):
"""Return the supplied endpoint"""
return self.endpoint
def get_token(self, session):
"""Return the supplied token"""
return self.token
def get_auth_ref(self, session, **kwargs):
"""Stub this method for compatibility"""
return None
# Override this because it needs to be a class method...
@classmethod
def get_options(self):
options = super(TokenEndpoint, self).get_options()
options.extend([
# Maintain name 'url' for compatibility
cfg.StrOpt('url',
help='Specific service endpoint to use'),
cfg.StrOpt('token',
secret=True,
help='Authentication token to use'),
])
return options
class OSCGenericPassword(ksc_password.Password):
"""Auth plugin hack to work around broken Keystone configurations
The default Keystone configuration uses http://localhost:xxxx in
admin_endpoint and public_endpoint and are returned in the links.href
attribute by the version routes. Deployments that do not set these
are unusable with newer keystoneclient version discovery.
"""
def create_plugin(self, session, version, url, raw_status=None):
"""Handle default Keystone endpoint configuration
Build the actual API endpoint from the scheme, host and port of the
original auth URL and the rest from the returned version URL.
"""
ver_u = urlparse.urlparse(url)
# Only hack this if it is the default setting
if ver_u.netloc.startswith('localhost'):
auth_u = urlparse.urlparse(self.auth_url)
# from original auth_url: scheme, netloc
# from api_url: path, query (basically, the rest)
url = urlparse.urlunparse((
auth_u.scheme,
auth_u.netloc,
ver_u.path,
ver_u.params,
ver_u.query,
ver_u.fragment,
))
LOG.debug('Version URL updated: %s' % url)
return super(OSCGenericPassword, self).create_plugin(
session=session,
version=version,
url=url,
raw_status=raw_status,
)

View File

@ -120,7 +120,7 @@ class ClientManager(object):
# password auth is requested.
if (self.auth_plugin_name.endswith('password') and
not self._cli_options.os_password):
self._cli_options.os_password = self.pw_callback()
self._cli_options.os_password = self._pw_callback()
(auth_plugin, self._auth_params) = auth.build_auth_params(
self.auth_plugin_name,

View File

@ -188,12 +188,6 @@ class OpenStackShell(app.App):
description,
version)
# service token auth argument
parser.add_argument(
'--os-url',
metavar='<url>',
default=utils.env('OS_URL'),
help='Defaults to env[OS_URL]')
# Global arguments
parser.add_argument(
'--os-region-name',

View File

@ -20,6 +20,7 @@ from keystoneclient import service_catalog
from oslo_serialization import jsonutils
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
@ -100,7 +101,7 @@ class TestClientManager(utils.TestCase):
)
self.assertIsInstance(
client_manager.auth,
auth.TokenEndpoint,
auth_plugin.TokenEndpoint,
)
self.assertFalse(client_manager._insecure)
self.assertTrue(client_manager._verify)

View File

@ -28,8 +28,8 @@ console_scripts =
openstack = openstackclient.shell:main
keystoneclient.auth.plugin =
token_endpoint = openstackclient.api.auth:TokenEndpoint
osc_password = openstackclient.api.auth:OSCGenericPassword
token_endpoint = openstackclient.api.auth_plugin:TokenEndpoint
osc_password = openstackclient.api.auth_plugin:OSCGenericPassword
openstack.cli =
command_list = openstackclient.common.module:ListCommand