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:
parent
4d8e1567ec
commit
cd58da5213
troveclient
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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user