Remove RAX-specific auth in troveclient

Author: Swapnil Kulkarni <swapnilkulkarni2608@gmail.com>
Co-Authored-By: Nikhil Manchanda <SlickNik@gmail.com>
Co-Authored-By: Craig Vyvial <cp16net@gmail.com>

Change-Id: I250777890a1f5240c5f14290cf02eb5a7b34b434
Closes-Bug: #966329
This commit is contained in:
Swapnil Kulkarni 2014-01-25 08:25:25 +05:30 committed by Craig Vyvial
parent 4d8e1567ec
commit cd58da5213
4 changed files with 173 additions and 43 deletions

107
troveclient/auth_plugin.py Normal file

@ -0,0 +1,107 @@
# Copyright 2014 Rackspace
# Copyright 2013 OpenStack Foundation
# Copyright 2013 Spanish National Research Council.
# All Rights Reserved.
#
# 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 logging
import pkg_resources
import six
from troveclient import exceptions
from troveclient.openstack.common.gettextutils import _ # noqa
logger = logging.getLogger(__name__)
_discovered_plugins = {}
def discover_auth_systems():
"""Discover the available auth-systems.
This won't take into account the old style auth-systems.
"""
ep_name = 'openstack.client.auth_plugin'
for ep in pkg_resources.iter_entry_points(ep_name):
try:
auth_plugin = ep.load()
except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e:
logger.debug(_("ERROR: Cannot load auth plugin %s") % ep.name)
logger.debug(e, exc_info=1)
else:
_discovered_plugins[ep.name] = auth_plugin
def load_auth_system_opts(parser):
"""Load options needed by the available auth-systems into a parser.
This function will try to populate the parser with options from the
available plugins.
"""
for name, auth_plugin in six.iteritems(_discovered_plugins):
add_opts_fn = getattr(auth_plugin, "add_opts", None)
if add_opts_fn:
group = parser.add_argument_group("Auth-system '%s' options" %
name)
add_opts_fn(group)
def load_plugin(auth_system):
if auth_system in _discovered_plugins:
return _discovered_plugins[auth_system]()
raise exceptions.AuthSystemNotFound(auth_system)
class BaseAuthPlugin(object):
"""Base class for authentication plugins.
An authentication plugin needs to override at least the authenticate
method to be a valid plugin.
"""
def __init__(self):
self.opts = {}
def get_auth_url(self):
"""Return the auth url for the plugin (if any)."""
return None
@staticmethod
def add_opts(parser):
"""Populate and return the parser with the options for this plugin.
If the plugin does not need any options, it should return the same
parser untouched.
"""
return parser
def parse_opts(self, args):
"""Parse the actual auth-system options if any.
This method is expected to populate the attribute self.opts with a
dict containing the options and values needed to make authentication.
If the dict is empty, the client should assume that it needs the same
options as the 'keystone' auth system (i.e. os_username and
os_password).
Returns the self.opts dict.
"""
return self.opts
def authenticate(self, cls, auth_url):
"""Authenticate using plugin defined method."""
raise exceptions.AuthSystemNotFound(self.auth_system)

@ -22,7 +22,6 @@ OpenStack Client interface. Handles the REST calls and responses.
from __future__ import print_function from __future__ import print_function
import logging import logging
import os
import requests import requests
from keystoneclient import adapter from keystoneclient import adapter
@ -89,7 +88,16 @@ class HTTPClient(TroveClientMixin):
self.password = password self.password = password
self.projectid = projectid self.projectid = projectid
self.tenant_id = tenant_id self.tenant_id = tenant_id
self.auth_url = auth_url.rstrip('/')
if auth_system and auth_system != 'keystone' and not auth_plugin:
raise exceptions.AuthSystemNotFound(auth_system)
if not auth_url and auth_system and auth_system != 'keystone':
auth_url = auth_plugin.get_auth_url()
if not auth_url:
raise exceptions.EndpointNotFound()
self.auth_url = auth_url.rstrip('/') if auth_url else auth_url
self.version = 'v1' self.version = 'v1'
self.region_name = region_name self.region_name = region_name
self.endpoint_type = endpoint_type self.endpoint_type = endpoint_type
@ -105,6 +113,8 @@ class HTTPClient(TroveClientMixin):
self.proxy_tenant_id = proxy_tenant_id self.proxy_tenant_id = proxy_tenant_id
self.timeout = timeout self.timeout = timeout
self.bypass_url = bypass_url self.bypass_url = bypass_url
self.auth_system = auth_system
self.auth_plugin = auth_plugin
if insecure: if insecure:
self.verify_cert = False self.verify_cert = False
@ -326,10 +336,10 @@ class HTTPClient(TroveClientMixin):
auth_url = self.auth_url auth_url = self.auth_url
if self.version == "v2.0": if self.version == "v2.0":
while auth_url: while auth_url:
if "TROVE_RAX_AUTH" in os.environ: if not self.auth_system or self.auth_system == 'keystone':
auth_url = self._rax_auth(auth_url)
else:
auth_url = self._v2_auth(auth_url) auth_url = self._v2_auth(auth_url)
else:
auth_url = self._plugin_auth(auth_url)
# Are we acting on behalf of another user via an # Are we acting on behalf of another user via an
# existing token? If so, our actual endpoints may # existing token? If so, our actual endpoints may
@ -357,6 +367,9 @@ class HTTPClient(TroveClientMixin):
if self.bypass_url is not None and self.bypass_url != '': if self.bypass_url is not None and self.bypass_url != '':
self.management_url = self.bypass_url self.management_url = self.bypass_url
def _plugin_auth(self, auth_url):
return self.auth_plugin.authenticate(self, auth_url)
def _v1_auth(self, url): def _v1_auth(self, url):
if self.proxy_token: if self.proxy_token:
raise exceptions.NoTokenLookupException() raise exceptions.NoTokenLookupException()
@ -393,16 +406,6 @@ class HTTPClient(TroveClientMixin):
self._authenticate(url, body) self._authenticate(url, body)
def _rax_auth(self, url):
"""Authenticate against the Rackspace auth service."""
body = {"auth": {
"RAX-KSKEY:apiKeyCredentials": {
"username": self.user,
"apiKey": self.password,
"tenantName": self.projectid}}}
self._authenticate(url, body)
def _authenticate(self, url, body): def _authenticate(self, url, body):
"""Authenticate and extract the service catalog.""" """Authenticate and extract the service catalog."""
token_url = url + "/tokens" token_url = url + "/tokens"

@ -21,6 +21,7 @@ Command-line interface to the OpenStack Trove API.
from __future__ import print_function from __future__ import print_function
import argparse import argparse
import getpass
import glob import glob
import imp import imp
import itertools import itertools
@ -38,9 +39,9 @@ from keystoneclient.auth.identity import v3 as identity
from keystoneclient import session as ks_session from keystoneclient import session as ks_session
import troveclient import troveclient
import troveclient.extension import troveclient.auth_plugin
from troveclient import client from troveclient import client
import troveclient.extension
from troveclient.openstack.common.apiclient import exceptions as exc from troveclient.openstack.common.apiclient import exceptions as exc
from troveclient.openstack.common import gettextutils as gtu from troveclient.openstack.common import gettextutils as gtu
from troveclient.openstack.common.gettextutils import _ # noqa from troveclient.openstack.common.gettextutils import _ # noqa
@ -107,7 +108,10 @@ class OpenStackTroveShell(object):
parser.add_argument('--os-auth-system', parser.add_argument('--os-auth-system',
metavar='<auth-system>', metavar='<auth-system>',
default=utils.env('OS_AUTH_SYSTEM')) default=utils.env('OS_AUTH_SYSTEM'),
help='Defaults to env[OS_AUTH_SYSTEM].')
parser.add_argument('--os_auth_system',
help=argparse.SUPPRESS)
parser.add_argument('--service-type', parser.add_argument('--service-type',
metavar='<service-type>', metavar='<service-type>',
@ -175,6 +179,9 @@ class OpenStackTroveShell(object):
self._append_global_identity_args(parser) self._append_global_identity_args(parser)
# The auth-system-plugins might require some extra options
troveclient.auth_plugin.load_auth_system_opts(parser)
return parser return parser
def _append_global_identity_args(self, parser): def _append_global_identity_args(self, parser):
@ -332,6 +339,9 @@ class OpenStackTroveShell(object):
self.setup_debugging(options.debug) self.setup_debugging(options.debug)
self.options = options self.options = options
# Discover available auth plugins
troveclient.auth_plugin.discover_auth_systems()
# build available subcommands based on version # build available subcommands based on version
self.extensions = self._discover_extensions( self.extensions = self._discover_extensions(
options.os_database_api_version) options.os_database_api_version)
@ -356,17 +366,20 @@ class OpenStackTroveShell(object):
self.do_bash_completion(args) self.do_bash_completion(args)
return 0 return 0
(os_username, os_password, os_tenant_name, os_auth_url, os_username = args.os_username
os_region_name, os_tenant_id, endpoint_type, insecure, os_password = args.os_password
service_type, service_name, database_service_name, os_tenant_name = args.os_tenant_name
cacert, bypass_url, os_auth_system) = ( os_auth_url = args.os_auth_url
args.os_username, args.os_password, os_region_name = args.os_region_name
args.os_tenant_name, args.os_auth_url, os_tenant_id = args.os_tenant_id
args.os_region_name, args.os_tenant_id, os_auth_system = args.os_auth_system
args.endpoint_type, args.insecure, endpoint_type = args.endpoint_type
args.service_type, args.service_name, insecure = args.insecure
args.database_service_name, service_type = args.service_type
args.os_cacert, args.bypass_url, args.os_auth_system) service_name = args.service_name
database_service_name = args.database_service_name
cacert = args.os_cacert
bypass_url = args.bypass_url
if os_auth_system and os_auth_system != "keystone": if os_auth_system and os_auth_system != "keystone":
auth_plugin = troveclient.auth_plugin.load_plugin(os_auth_system) auth_plugin = troveclient.auth_plugin.load_plugin(os_auth_system)
@ -384,20 +397,22 @@ class OpenStackTroveShell(object):
# for os_username or os_password but for compatibility it is not. # for os_username or os_password but for compatibility it is not.
if not utils.isunauthenticated(args.func): if not utils.isunauthenticated(args.func):
if not os_username:
raise exc.CommandError( if auth_plugin:
"You must provide a username " auth_plugin.parse_opts(args)
"via either --os-username or env[OS_USERNAME]")
if not auth_plugin or not auth_plugin.opts:
if not os_username:
raise exc.CommandError(
"You must provide a username "
"via either --os-username or env[OS_USERNAME]")
if not os_password: if not os_password:
raise exc.CommandError("You must provide a password " os_password = getpass.getpass()
"via either --os-password or via "
"env[OS_PASSWORD]")
if not os_auth_url: if not os_auth_url:
raise exc.CommandError( if os_auth_system and os_auth_system != 'keystone':
"You must provide an auth url " os_auth_url = auth_plugin.get_auth_url()
"via either --os-auth-url or env[OS_AUTH_URL]")
# V3 stuff # V3 stuff
project_info_provided = (self.options.os_tenant_name or project_info_provided = (self.options.os_tenant_name or
@ -422,9 +437,12 @@ class OpenStackTroveShell(object):
"(env[OS_PROJECT_DOMAIN_NAME])")) "(env[OS_PROJECT_DOMAIN_NAME])"))
if not os_auth_url: if not os_auth_url:
raise exc.CommandError( raise exc.CommandError("You must provide an auth url "
"You must provide an auth url " "via either --os-auth-url or "
"via either --os-auth-url or env[OS_AUTH_URL]") "env[OS_AUTH_URL] or specify an "
"auth_system which defines a default "
"url with --os-auth-system or "
"env[OS_AUTH_SYSTEM]")
use_session = True use_session = True
if auth_plugin or bypass_url: if auth_plugin or bypass_url:

@ -150,7 +150,9 @@ class ShellTest(testtools.TestCase):
def test_no_auth_url(self): def test_no_auth_url(self):
required = ('You must provide an auth url' required = ('You must provide an auth url'
' via either --os-auth-url or env[OS_AUTH_URL]',) ' via either --os-auth-url or env[OS_AUTH_URL] '
'or specify an auth_system which defines a default '
'url with --os-auth-system or env[OS_AUTH_SYSTEM]',)
self.make_env(exclude='OS_AUTH_URL') self.make_env(exclude='OS_AUTH_URL')
try: try:
self.shell('list') self.shell('list')