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:
		
				
					committed by
					
						
						Craig Vyvial
					
				
			
			
				
	
			
			
			
						parent
						
							4d8e1567ec
						
					
				
				
					commit
					cd58da5213
				
			
							
								
								
									
										107
									
								
								troveclient/auth_plugin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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
 | 
			
		||||
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import requests
 | 
			
		||||
 | 
			
		||||
from keystoneclient import adapter
 | 
			
		||||
@@ -89,7 +88,16 @@ class HTTPClient(TroveClientMixin):
 | 
			
		||||
        self.password = password
 | 
			
		||||
        self.projectid = projectid
 | 
			
		||||
        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.region_name = region_name
 | 
			
		||||
        self.endpoint_type = endpoint_type
 | 
			
		||||
@@ -105,6 +113,8 @@ class HTTPClient(TroveClientMixin):
 | 
			
		||||
        self.proxy_tenant_id = proxy_tenant_id
 | 
			
		||||
        self.timeout = timeout
 | 
			
		||||
        self.bypass_url = bypass_url
 | 
			
		||||
        self.auth_system = auth_system
 | 
			
		||||
        self.auth_plugin = auth_plugin
 | 
			
		||||
 | 
			
		||||
        if insecure:
 | 
			
		||||
            self.verify_cert = False
 | 
			
		||||
@@ -326,10 +336,10 @@ class HTTPClient(TroveClientMixin):
 | 
			
		||||
        auth_url = self.auth_url
 | 
			
		||||
        if self.version == "v2.0":
 | 
			
		||||
            while auth_url:
 | 
			
		||||
                if "TROVE_RAX_AUTH" in os.environ:
 | 
			
		||||
                    auth_url = self._rax_auth(auth_url)
 | 
			
		||||
                else:
 | 
			
		||||
                if not self.auth_system or self.auth_system == 'keystone':
 | 
			
		||||
                    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
 | 
			
		||||
            # 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 != '':
 | 
			
		||||
            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):
 | 
			
		||||
        if self.proxy_token:
 | 
			
		||||
            raise exceptions.NoTokenLookupException()
 | 
			
		||||
@@ -393,16 +406,6 @@ class HTTPClient(TroveClientMixin):
 | 
			
		||||
 | 
			
		||||
        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):
 | 
			
		||||
        """Authenticate and extract the service catalog."""
 | 
			
		||||
        token_url = url + "/tokens"
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ Command-line interface to the OpenStack Trove API.
 | 
			
		||||
from __future__ import print_function
 | 
			
		||||
 | 
			
		||||
import argparse
 | 
			
		||||
import getpass
 | 
			
		||||
import glob
 | 
			
		||||
import imp
 | 
			
		||||
import itertools
 | 
			
		||||
@@ -38,9 +39,9 @@ from keystoneclient.auth.identity import v3 as identity
 | 
			
		||||
from keystoneclient import session as ks_session
 | 
			
		||||
 | 
			
		||||
import troveclient
 | 
			
		||||
import troveclient.extension
 | 
			
		||||
 | 
			
		||||
import troveclient.auth_plugin
 | 
			
		||||
from troveclient import client
 | 
			
		||||
import troveclient.extension
 | 
			
		||||
from troveclient.openstack.common.apiclient import exceptions as exc
 | 
			
		||||
from troveclient.openstack.common import gettextutils as gtu
 | 
			
		||||
from troveclient.openstack.common.gettextutils import _  # noqa
 | 
			
		||||
@@ -107,7 +108,10 @@ class OpenStackTroveShell(object):
 | 
			
		||||
 | 
			
		||||
        parser.add_argument('--os-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',
 | 
			
		||||
                            metavar='<service-type>',
 | 
			
		||||
@@ -175,6 +179,9 @@ class OpenStackTroveShell(object):
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
    def _append_global_identity_args(self, parser):
 | 
			
		||||
@@ -332,6 +339,9 @@ class OpenStackTroveShell(object):
 | 
			
		||||
        self.setup_debugging(options.debug)
 | 
			
		||||
        self.options = options
 | 
			
		||||
 | 
			
		||||
        # Discover available auth plugins
 | 
			
		||||
        troveclient.auth_plugin.discover_auth_systems()
 | 
			
		||||
 | 
			
		||||
        # build available subcommands based on version
 | 
			
		||||
        self.extensions = self._discover_extensions(
 | 
			
		||||
            options.os_database_api_version)
 | 
			
		||||
@@ -356,17 +366,20 @@ class OpenStackTroveShell(object):
 | 
			
		||||
            self.do_bash_completion(args)
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
        (os_username, os_password, os_tenant_name, os_auth_url,
 | 
			
		||||
         os_region_name, os_tenant_id, endpoint_type, insecure,
 | 
			
		||||
         service_type, service_name, database_service_name,
 | 
			
		||||
         cacert, bypass_url, os_auth_system) = (
 | 
			
		||||
             args.os_username, args.os_password,
 | 
			
		||||
             args.os_tenant_name, args.os_auth_url,
 | 
			
		||||
             args.os_region_name, args.os_tenant_id,
 | 
			
		||||
             args.endpoint_type, args.insecure,
 | 
			
		||||
             args.service_type, args.service_name,
 | 
			
		||||
             args.database_service_name,
 | 
			
		||||
             args.os_cacert, args.bypass_url, args.os_auth_system)
 | 
			
		||||
        os_username = args.os_username
 | 
			
		||||
        os_password = args.os_password
 | 
			
		||||
        os_tenant_name = args.os_tenant_name
 | 
			
		||||
        os_auth_url = args.os_auth_url
 | 
			
		||||
        os_region_name = args.os_region_name
 | 
			
		||||
        os_tenant_id = args.os_tenant_id
 | 
			
		||||
        os_auth_system = args.os_auth_system
 | 
			
		||||
        endpoint_type = args.endpoint_type
 | 
			
		||||
        insecure = args.insecure
 | 
			
		||||
        service_type = args.service_type
 | 
			
		||||
        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":
 | 
			
		||||
            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.
 | 
			
		||||
 | 
			
		||||
        if not utils.isunauthenticated(args.func):
 | 
			
		||||
            if not os_username:
 | 
			
		||||
                raise exc.CommandError(
 | 
			
		||||
                    "You must provide a username "
 | 
			
		||||
                    "via either --os-username or env[OS_USERNAME]")
 | 
			
		||||
 | 
			
		||||
            if auth_plugin:
 | 
			
		||||
                auth_plugin.parse_opts(args)
 | 
			
		||||
 | 
			
		||||
            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:
 | 
			
		||||
                raise exc.CommandError("You must provide a password "
 | 
			
		||||
                                       "via either --os-password or via "
 | 
			
		||||
                                       "env[OS_PASSWORD]")
 | 
			
		||||
                os_password = getpass.getpass()
 | 
			
		||||
 | 
			
		||||
            if not os_auth_url:
 | 
			
		||||
                raise exc.CommandError(
 | 
			
		||||
                    "You must provide an auth url "
 | 
			
		||||
                    "via either --os-auth-url or env[OS_AUTH_URL]")
 | 
			
		||||
                if os_auth_system and os_auth_system != 'keystone':
 | 
			
		||||
                    os_auth_url = auth_plugin.get_auth_url()
 | 
			
		||||
 | 
			
		||||
        # V3 stuff
 | 
			
		||||
        project_info_provided = (self.options.os_tenant_name or
 | 
			
		||||
@@ -422,9 +437,12 @@ class OpenStackTroveShell(object):
 | 
			
		||||
                  "(env[OS_PROJECT_DOMAIN_NAME])"))
 | 
			
		||||
 | 
			
		||||
        if not os_auth_url:
 | 
			
		||||
            raise exc.CommandError(
 | 
			
		||||
                "You must provide an auth url "
 | 
			
		||||
                "via either --os-auth-url or env[OS_AUTH_URL]")
 | 
			
		||||
            raise exc.CommandError("You must provide an 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]")
 | 
			
		||||
 | 
			
		||||
        use_session = True
 | 
			
		||||
        if auth_plugin or bypass_url:
 | 
			
		||||
 
 | 
			
		||||
@@ -150,7 +150,9 @@ class ShellTest(testtools.TestCase):
 | 
			
		||||
 | 
			
		||||
    def test_no_auth_url(self):
 | 
			
		||||
        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')
 | 
			
		||||
        try:
 | 
			
		||||
            self.shell('list')
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user