Merge "Remove support for non-keystone auth systems"
This commit is contained in:
commit
0a0254f38e
@ -1,150 +0,0 @@
|
||||
# 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 novaclient import exceptions
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
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:
|
||||
# FIXME(dhellmann): It would be better to use stevedore
|
||||
# here, since it abstracts this difference in behavior
|
||||
# between versions of setuptools, but this seemed like a
|
||||
# more expedient fix.
|
||||
if hasattr(ep, 'resolve') and hasattr(ep, 'require'):
|
||||
auth_plugin = ep.resolve()
|
||||
else:
|
||||
auth_plugin = ep.load(require=False)
|
||||
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]()
|
||||
|
||||
# NOTE(aloga): If we arrive here, the plugin will be an old-style one,
|
||||
# so we have to create a fake AuthPlugin for it.
|
||||
return DeprecatedAuthPlugin(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)
|
||||
|
||||
|
||||
class DeprecatedAuthPlugin(object):
|
||||
"""Class to mimic the AuthPlugin class for deprecated auth systems.
|
||||
|
||||
Old auth systems only define two entry points: openstack.client.auth_url
|
||||
and openstack.client.authenticate. This class will load those entry points
|
||||
into a class similar to a valid AuthPlugin.
|
||||
"""
|
||||
def __init__(self, auth_system):
|
||||
self.auth_system = auth_system
|
||||
|
||||
def authenticate(cls, auth_url):
|
||||
raise exceptions.AuthSystemNotFound(self.auth_system)
|
||||
|
||||
self.opts = {}
|
||||
|
||||
self.get_auth_url = lambda: None
|
||||
self.authenticate = authenticate
|
||||
|
||||
self._load_endpoints()
|
||||
|
||||
def _load_endpoints(self):
|
||||
ep_name = 'openstack.client.auth_url'
|
||||
fn = utils.load_entry_point(ep_name, name=self.auth_system)
|
||||
if fn:
|
||||
self.get_auth_url = fn
|
||||
|
||||
ep_name = 'openstack.client.authenticate'
|
||||
fn = utils.load_entry_point(ep_name, name=self.auth_system)
|
||||
if fn:
|
||||
self.authenticate = fn
|
||||
|
||||
def parse_opts(self, args):
|
||||
return self.opts
|
@ -156,8 +156,7 @@ class HTTPClient(object):
|
||||
service_name=None, volume_service_name=None,
|
||||
timings=False, bypass_url=None,
|
||||
os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None, auth_token=None,
|
||||
http_log_debug=False, auth_token=None,
|
||||
cacert=None, tenant_id=None, user_id=None,
|
||||
connection_pool=False, api_version=None,
|
||||
logger=None):
|
||||
@ -177,13 +176,6 @@ class HTTPClient(object):
|
||||
# been proven invalid
|
||||
self.password_func = None
|
||||
|
||||
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.1'
|
||||
self.region_name = region_name
|
||||
@ -217,8 +209,6 @@ class HTTPClient(object):
|
||||
else:
|
||||
self.verify_cert = True
|
||||
|
||||
self.auth_system = auth_system
|
||||
self.auth_plugin = auth_plugin
|
||||
self._session = None
|
||||
self._current_url = None
|
||||
self._logger = logger or logging.getLogger(__name__)
|
||||
@ -589,10 +579,7 @@ class HTTPClient(object):
|
||||
auth_url = self.auth_url
|
||||
if self.version == "v2.0": # FIXME(chris): This should be better.
|
||||
while auth_url:
|
||||
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)
|
||||
auth_url = self._v2_auth(auth_url)
|
||||
|
||||
# Are we acting on behalf of another user via an
|
||||
# existing token? If so, our actual endpoints may
|
||||
@ -659,9 +646,6 @@ class HTTPClient(object):
|
||||
else:
|
||||
raise exceptions.from_response(resp, body, url)
|
||||
|
||||
def _plugin_auth(self, auth_url):
|
||||
return self.auth_plugin.authenticate(self, auth_url)
|
||||
|
||||
def _v2_auth(self, url):
|
||||
"""Authenticate against a v2.0 auth service."""
|
||||
if self.auth_token:
|
||||
@ -707,7 +691,6 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
||||
service_name=None, volume_service_name=None,
|
||||
timings=False, bypass_url=None, os_cache=False,
|
||||
no_cache=True, http_log_debug=False,
|
||||
auth_system='keystone', auth_plugin=None,
|
||||
auth_token=None, cacert=None, tenant_id=None,
|
||||
user_id=None, connection_pool=False, session=None,
|
||||
auth=None, user_agent='python-novaclient',
|
||||
@ -738,8 +721,6 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
|
@ -54,15 +54,6 @@ class NoUniqueMatch(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AuthSystemNotFound(Exception):
|
||||
"""When the user specify a AuthSystem but not installed."""
|
||||
def __init__(self, auth_system):
|
||||
self.auth_system = auth_system
|
||||
|
||||
def __str__(self):
|
||||
return "AuthSystemNotFound: %s" % repr(self.auth_system)
|
||||
|
||||
|
||||
class NoTokenLookupException(Exception):
|
||||
"""This form of authentication does not support looking up
|
||||
endpoints from an existing token.
|
||||
|
@ -23,7 +23,6 @@ import argparse
|
||||
import getpass
|
||||
import logging
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from keystoneauth1 import loading
|
||||
from oslo_utils import encodeutils
|
||||
@ -41,7 +40,6 @@ except ImportError:
|
||||
|
||||
import novaclient
|
||||
from novaclient import api_versions
|
||||
import novaclient.auth_plugin
|
||||
from novaclient import client
|
||||
from novaclient import exceptions as exc
|
||||
import novaclient.extension
|
||||
@ -455,12 +453,6 @@ class OpenStackComputeShell(object):
|
||||
default=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
|
||||
help=_('Defaults to env[OS_REGION_NAME].'))
|
||||
|
||||
parser.add_argument(
|
||||
'--os-auth-system',
|
||||
metavar='<auth-system>',
|
||||
default=utils.env('OS_AUTH_SYSTEM'),
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument(
|
||||
'--service-type',
|
||||
metavar='<service-type>',
|
||||
@ -509,9 +501,6 @@ class OpenStackComputeShell(object):
|
||||
help=_("Use this API endpoint instead of the Service Catalog. "
|
||||
"Defaults to env[NOVACLIENT_BYPASS_URL]."))
|
||||
|
||||
# The auth-system-plugins might require some extra options
|
||||
novaclient.auth_plugin.load_auth_system_opts(parser)
|
||||
|
||||
self._append_global_identity_args(parser, argv)
|
||||
|
||||
return parser
|
||||
@ -639,9 +628,6 @@ class OpenStackComputeShell(object):
|
||||
skip_auth = do_help or (
|
||||
'bash-completion' in argv)
|
||||
|
||||
# Discover available auth plugins
|
||||
novaclient.auth_plugin.discover_auth_systems()
|
||||
|
||||
if not args.os_compute_api_version:
|
||||
api_version = api_versions.get_api_version(
|
||||
DEFAULT_MAJOR_OS_COMPUTE_API_VERSION)
|
||||
@ -658,7 +644,6 @@ class OpenStackComputeShell(object):
|
||||
args, 'os_project_id', getattr(args, 'os_tenant_id', None))
|
||||
os_auth_url = args.os_auth_url
|
||||
os_region_name = args.os_region_name
|
||||
os_auth_system = args.os_auth_system
|
||||
|
||||
if "v2.0" not in os_auth_url:
|
||||
# NOTE(andreykurilin): assume that keystone V3 is used and try to
|
||||
@ -691,15 +676,6 @@ class OpenStackComputeShell(object):
|
||||
auth_token = getattr(args, 'os_token', None)
|
||||
management_url = bypass_url if bypass_url else None
|
||||
|
||||
if os_auth_system and os_auth_system != "keystone":
|
||||
warnings.warn(_(
|
||||
'novaclient auth plugins that are not keystone are deprecated.'
|
||||
' Auth plugins should now be done as plugins to keystoneauth'
|
||||
' and selected with --os-auth-type or OS_AUTH_TYPE'))
|
||||
auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_system)
|
||||
else:
|
||||
auth_plugin = None
|
||||
|
||||
if not endpoint_type:
|
||||
endpoint_type = DEFAULT_NOVA_ENDPOINT_TYPE
|
||||
|
||||
@ -717,25 +693,20 @@ class OpenStackComputeShell(object):
|
||||
# Expired tokens are handled by client.py:_cs_request
|
||||
must_auth = not (auth_token and management_url)
|
||||
|
||||
# Do not use Keystone session for cases with no session support. The
|
||||
# presence of auth_plugin means os_auth_system is present and is not
|
||||
# keystone.
|
||||
# Do not use Keystone session for cases with no session support.
|
||||
use_session = True
|
||||
if auth_plugin or bypass_url or os_cache or volume_service_name:
|
||||
if bypass_url or os_cache or volume_service_name:
|
||||
use_session = False
|
||||
|
||||
# FIXME(usrleon): Here should be restrict for project id same as
|
||||
# for os_username or os_password but for compatibility it is not.
|
||||
if must_auth and not skip_auth:
|
||||
if auth_plugin:
|
||||
auth_plugin.parse_opts(args)
|
||||
|
||||
if not auth_plugin or not auth_plugin.opts:
|
||||
if not os_username and not os_user_id:
|
||||
raise exc.CommandError(
|
||||
_("You must provide a username "
|
||||
"or user ID via --os-username, --os-user-id, "
|
||||
"env[OS_USERNAME] or env[OS_USER_ID]"))
|
||||
if not os_username and not os_user_id:
|
||||
raise exc.CommandError(
|
||||
_("You must provide a username "
|
||||
"or user ID via --os-username, --os-user-id, "
|
||||
"env[OS_USERNAME] or env[OS_USER_ID]"))
|
||||
|
||||
if not any([os_project_name, os_project_id]):
|
||||
raise exc.CommandError(_("You must provide a project name or"
|
||||
@ -746,16 +717,9 @@ class OpenStackComputeShell(object):
|
||||
" interchangeably."))
|
||||
|
||||
if not os_auth_url:
|
||||
if os_auth_system and os_auth_system != 'keystone':
|
||||
os_auth_url = auth_plugin.get_auth_url()
|
||||
|
||||
if not 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]"))
|
||||
raise exc.CommandError(
|
||||
_("You must provide an auth url "
|
||||
"via either --os-auth-url or env[OS_AUTH_URL]."))
|
||||
|
||||
if use_session:
|
||||
# Not using Nova auth plugin, so use keystone
|
||||
@ -792,8 +756,7 @@ class OpenStackComputeShell(object):
|
||||
auth_url=os_auth_url, insecure=insecure,
|
||||
region_name=os_region_name, endpoint_type=endpoint_type,
|
||||
extensions=self.extensions, service_type=service_type,
|
||||
service_name=service_name, auth_system=os_auth_system,
|
||||
auth_plugin=auth_plugin, auth_token=auth_token,
|
||||
service_name=service_name, auth_token=auth_token,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=args.timings, bypass_url=bypass_url,
|
||||
os_cache=os_cache, http_log_debug=args.debug,
|
||||
@ -857,8 +820,7 @@ class OpenStackComputeShell(object):
|
||||
auth_url=os_auth_url, insecure=insecure,
|
||||
region_name=os_region_name, endpoint_type=endpoint_type,
|
||||
extensions=self.extensions, service_type=service_type,
|
||||
service_name=service_name, auth_system=os_auth_system,
|
||||
auth_plugin=auth_plugin, auth_token=auth_token,
|
||||
service_name=service_name, auth_token=auth_token,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=args.timings, bypass_url=bypass_url,
|
||||
os_cache=os_cache, http_log_debug=args.debug,
|
||||
@ -870,11 +832,6 @@ class OpenStackComputeShell(object):
|
||||
if must_auth:
|
||||
helper = SecretsHelper(args, self.cs.client)
|
||||
self.cs.client.keyring_saver = helper
|
||||
if (auth_plugin and auth_plugin.opts and
|
||||
"os_password" not in auth_plugin.opts):
|
||||
use_pw = False
|
||||
else:
|
||||
use_pw = True
|
||||
|
||||
tenant_id = helper.tenant_id
|
||||
# Allow commandline to override cache
|
||||
@ -887,7 +844,7 @@ class OpenStackComputeShell(object):
|
||||
self.cs.client.auth_token = auth_token
|
||||
self.cs.client.management_url = management_url
|
||||
self.cs.client.password_func = lambda: helper.password
|
||||
elif use_pw:
|
||||
else:
|
||||
# We're missing something, so auth with user/pass and save
|
||||
# the result in our helper.
|
||||
self.cs.client.password = helper.password
|
||||
|
@ -1,358 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# 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 argparse
|
||||
|
||||
from keystoneauth1 import fixture
|
||||
import mock
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
from novaclient import auth_plugin
|
||||
from novaclient import client
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
def mock_http_request(resp=None):
|
||||
"""Mock an HTTP Request."""
|
||||
if not resp:
|
||||
resp = fixture.V2Token()
|
||||
resp.set_scope()
|
||||
s = resp.add_service('compute')
|
||||
s.add_endpoint("http://localhost:8774/v1.1", region='RegionOne')
|
||||
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
return mock.Mock(return_value=(auth_response))
|
||||
|
||||
|
||||
def requested_headers(cs):
|
||||
"""Return requested passed headers."""
|
||||
return {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
|
||||
|
||||
class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
def test_auth_system_success(self):
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return self.authenticate
|
||||
|
||||
def resolve(self):
|
||||
return self.authenticate
|
||||
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
def mock_iter_entry_points(_type, name):
|
||||
if _type == 'openstack.client.authenticate':
|
||||
return [MockEntrypoint("fake", "fake", ["fake"])]
|
||||
else:
|
||||
return []
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fake")
|
||||
cs = client.Client("2", "username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
headers = requested_headers(cs)
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data='{"fake": "me"}',
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_system_not_exists(self):
|
||||
def mock_iter_entry_points(_t, name=None):
|
||||
return [pkg_resources.EntryPoint("fake", "fake", ["fake"])]
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("notexists")
|
||||
cs = client.Client("2", "username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="notexists",
|
||||
auth_plugin=plugin)
|
||||
self.assertRaises(exceptions.AuthSystemNotFound,
|
||||
cs.client.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_system_defining_auth_url(self):
|
||||
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return self.auth_url
|
||||
|
||||
def resolve(self):
|
||||
return self.auth_url
|
||||
|
||||
def auth_url(self):
|
||||
return "http://faked/v2.0"
|
||||
|
||||
class MockAuthenticateEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return self.authenticate
|
||||
|
||||
def resolve(self):
|
||||
return self.authenticate
|
||||
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
def mock_iter_entry_points(_type, name):
|
||||
if _type == 'openstack.client.auth_url':
|
||||
return [MockAuthUrlEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["auth_url"])]
|
||||
elif _type == 'openstack.client.authenticate':
|
||||
return [MockAuthenticateEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["authenticate"])]
|
||||
else:
|
||||
return []
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
||||
cs = client.Client("2", "username", "password", "project_id",
|
||||
auth_system="fakewithauthurl",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points):
|
||||
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return self.auth_url
|
||||
|
||||
def resolve(self):
|
||||
return self.auth_url
|
||||
|
||||
def auth_url(self):
|
||||
return None
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name: [
|
||||
MockAuthUrlEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["auth_url"])]
|
||||
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "2", "username", "password", "project_id",
|
||||
auth_system="fakewithauthurl", auth_plugin=plugin)
|
||||
|
||||
|
||||
class AuthPluginTest(utils.TestCase):
|
||||
@mock.patch.object(requests, "request")
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_auth_system_success(self, mock_iter_entry_points, mock_request):
|
||||
"""Test that we can authenticate using the auth system."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
mock_request.side_effect = mock_http_request()
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
cs = client.Client("2", "username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
headers = requested_headers(cs)
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data='{"fake": "me"}',
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_discover_auth_system_options(self, mock_iter_entry_points):
|
||||
"""Test that we can load the auth system options."""
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
@staticmethod
|
||||
def add_opts(parser):
|
||||
parser.add_argument('--auth_system_opt',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Fake option")
|
||||
return parser
|
||||
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
auth_plugin.discover_auth_systems()
|
||||
auth_plugin.load_auth_system_opts(parser)
|
||||
opts, args = parser.parse_known_args(['--auth_system_opt'])
|
||||
|
||||
self.assertTrue(opts.auth_system_opt)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_parse_auth_system_options(self, mock_iter_entry_points):
|
||||
"""Test that we can parse the auth system options."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def __init__(self):
|
||||
self.opts = {"fake_argument": True}
|
||||
|
||||
def parse_opts(self, args):
|
||||
return self.opts
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
plugin.parse_opts([])
|
||||
self.assertIn("fake_argument", plugin.opts)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_auth_system_defining_url(self, mock_iter_entry_points):
|
||||
"""Test the auth_system defining an url."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def get_auth_url(self):
|
||||
return "http://faked/v2.0"
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
cs = client.Client("2", "username", "password", "project_id",
|
||||
auth_system="fakewithauthurl",
|
||||
auth_plugin=plugin)
|
||||
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_exception_if_no_authenticate(self, mock_iter_entry_points):
|
||||
"""Test that no authenticate raises a proper exception."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
pass
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "2", "username", "password", "project_id",
|
||||
auth_system="fake", auth_plugin=plugin)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_exception_if_no_url(self, mock_iter_entry_points):
|
||||
"""Test that no auth_url at all raises exception."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self, require=False):
|
||||
return FakePlugin
|
||||
|
||||
def resolve(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
pass
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name=None: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "2", "username", "password", "project_id",
|
||||
auth_system="fake", auth_plugin=plugin)
|
@ -62,15 +62,7 @@ FAKE_ENV4 = {'OS_USER_ID': 'user_id',
|
||||
FAKE_ENV5 = {'OS_USERNAME': 'username',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_NAME': 'tenant_name',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0',
|
||||
'OS_COMPUTE_API_VERSION': '2',
|
||||
'OS_AUTH_SYSTEM': 'rackspace'}
|
||||
|
||||
FAKE_ENV6 = {'OS_USERNAME': 'username',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_NAME': 'tenant_name',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0',
|
||||
'OS_AUTH_SYSTEM': 'rackspace'}
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0'}
|
||||
|
||||
|
||||
def _create_ver_list(versions):
|
||||
@ -519,9 +511,7 @@ class ShellTest(utils.TestCase):
|
||||
|
||||
def test_no_auth_url(self):
|
||||
required = ('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]',)
|
||||
' via either --os-auth-url or env[OS_AUTH_URL].',)
|
||||
self.make_env(exclude='OS_AUTH_URL')
|
||||
try:
|
||||
self.shell('list')
|
||||
@ -687,7 +677,7 @@ class ShellTest(utils.TestCase):
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_microversion_with_default_behaviour(self, mock_client):
|
||||
self.make_env(fake_env=FAKE_ENV6)
|
||||
self.make_env(fake_env=FAKE_ENV5)
|
||||
self.mock_server_version_range.return_value = (
|
||||
api_versions.APIVersion("2.1"), api_versions.APIVersion("2.3"))
|
||||
self.shell('list')
|
||||
@ -697,7 +687,7 @@ class ShellTest(utils.TestCase):
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_microversion_with_default_behaviour_with_legacy_server(
|
||||
self, mock_client):
|
||||
self.make_env(fake_env=FAKE_ENV6)
|
||||
self.make_env(fake_env=FAKE_ENV5)
|
||||
self.mock_server_version_range.return_value = (
|
||||
api_versions.APIVersion(), api_versions.APIVersion())
|
||||
self.shell('list')
|
||||
@ -777,15 +767,6 @@ class ShellTest(utils.TestCase):
|
||||
self.shell,
|
||||
'--os-compute-api-version 2.3 list')
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_custom_auth_plugin(self, mock_client):
|
||||
self.make_env(fake_env=FAKE_ENV5)
|
||||
self.shell('list')
|
||||
password = mock_client.call_args_list[0][0][2]
|
||||
client_kwargs = mock_client.call_args_list[0][1]
|
||||
self.assertEqual(password, 'password')
|
||||
self.assertIs(client_kwargs['session'], None)
|
||||
|
||||
@mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main')
|
||||
def test_main_error_handling(self, mock_compute_shell):
|
||||
class MyException(Exception):
|
||||
|
@ -68,7 +68,7 @@ class Client(object):
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None, timings=False, bypass_url=None,
|
||||
os_cache=False, no_cache=True, http_log_debug=False,
|
||||
auth_system='keystone', auth_plugin=None, auth_token=None,
|
||||
auth_token=None,
|
||||
cacert=None, tenant_id=None, user_id=None,
|
||||
connection_pool=False, session=None, auth=None,
|
||||
api_version=None, direct_use=True, logger=None, **kwargs):
|
||||
@ -93,8 +93,6 @@ class Client(object):
|
||||
:param bool os_cache: OS cache
|
||||
:param bool no_cache: No cache
|
||||
:param bool http_log_debug: Enable debugging for HTTP connections
|
||||
:param str auth_system: Auth system
|
||||
:param str auth_plugin: Auth plugin
|
||||
:param str auth_token: Auth token
|
||||
:param str cacert: cacert
|
||||
:param str tenant_id: Tenant ID
|
||||
@ -194,8 +192,6 @@ class Client(object):
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
prelude: >
|
||||
The ability to use non-Keystone authentication systems has been removed.
|
||||
upgrade:
|
||||
- The ``--os-auth-system`` CLI option and ``OS_AUTH_SYSTEM`` environment
|
||||
variable usage was deprecated in the 3.1.0 release during the Mitaka
|
||||
development series. This release drops the support for using those options
|
||||
to load non-Keystone authentication systems via the
|
||||
``openstack.client.auth_plugin`` extension point.
|
Loading…
x
Reference in New Issue
Block a user