Merge "Use keystoneclient auth plugins"

This commit is contained in:
Jenkins
2015-01-28 06:59:00 +00:00
committed by Gerrit Code Review
11 changed files with 309 additions and 250 deletions

View File

@@ -52,14 +52,14 @@ class KeystonePasswordAuthProtocol(object):
ctx = context.RequestContext(username=username, password=password,
tenant_id=tenant, auth_url=auth_url,
is_admin=False)
hc = heat_keystoneclient.KeystoneClient(ctx)
client = hc.client
session = heat_keystoneclient.KeystoneClient(ctx).session
auth_ref = ctx.auth_plugin.get_access(session)
except (keystone_exceptions.Unauthorized,
keystone_exceptions.Forbidden,
keystone_exceptions.NotFound,
keystone_exceptions.AuthorizationFailure):
return self._reject_request(env, start_response, auth_url)
env.update(self._build_user_headers(client.auth_ref))
env.update(self._build_user_headers(auth_ref))
return self.app(env, start_response)

View File

@@ -11,16 +11,60 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystoneclient import access
from keystoneclient.auth.identity import base
from keystoneclient.auth.identity import v3
from keystoneclient.auth import token_endpoint
from oslo.config import cfg
from oslo.middleware import request_id as oslo_request_id
from oslo.utils import importutils
from oslo_context import context
from oslo_middleware import request_id as oslo_request_id
from heat.common import exception
from heat.common.i18n import _LE
from heat.common import policy
from heat.common import wsgi
from heat.db import api as db_api
from heat.engine import clients
from heat.openstack.common import local
from heat.openstack.common import log as logging
LOG = logging.getLogger(__name__)
# FIXME(jamielennox): I copied this out of a review that is proposed against
# keystoneclient which can be used when available.
# https://review.openstack.org/#/c/143338/
class _AccessInfoPlugin(base.BaseIdentityPlugin):
"""A plugin that turns an existing AccessInfo object into a usable plugin.
In certain circumstances you already have an auth_ref/AccessInfo object
that you just want to reuse. This could have been from a cache, in
auth_token middleware or other.
Turn that existing object into a simple identity plugin. This plugin cannot
be refreshed as the AccessInfo object does not contain any authorizing
information.
:param auth_ref: the existing AccessInfo object.
:type auth_ref: keystoneclient.access.AccessInfo
:param auth_url: the url where this AccessInfo was retrieved from. Required
if using the AUTH_INTERFACE with get_endpoint. (optional)
"""
def __init__(self, auth_url, auth_ref):
super(_AccessInfoPlugin, self).__init__(auth_url=auth_url,
reauthenticate=False)
self.auth_ref = auth_ref
def get_auth_ref(self, session, **kwargs):
return self.auth_ref
def invalidate(self):
# NOTE(jamielennox): Don't allow the default invalidation to occur
# because on next authentication request we will only get the same
# auth_ref object again.
return False
class RequestContext(context.RequestContext):
@@ -35,7 +79,7 @@ class RequestContext(context.RequestContext):
read_only=False, show_deleted=False,
overwrite=True, trust_id=None, trustor_user_id=None,
request_id=None, auth_token_info=None, region_name=None,
**kwargs):
auth_plugin=None, **kwargs):
"""
:param overwrite: Set to False to ensure that the greenthread local
copy of the index is not overwritten.
@@ -66,6 +110,7 @@ class RequestContext(context.RequestContext):
self.trust_id = trust_id
self.trustor_user_id = trustor_user_id
self.policy = policy.Enforcer()
self._auth_plugin = auth_plugin
if is_admin is None:
self.is_admin = self.policy.check_is_admin(self)
@@ -110,6 +155,59 @@ class RequestContext(context.RequestContext):
def from_dict(cls, values):
return cls(**values)
@property
def _keystone_v3_endpoint(self):
if self.auth_url:
auth_uri = self.auth_url
else:
importutils.import_module('keystonemiddleware.auth_token')
auth_uri = cfg.CONF.keystone_authtoken.auth_uri
return auth_uri.replace('v2.0', 'v3')
def _create_auth_plugin(self):
if self.trust_id:
importutils.import_module('keystonemiddleware.auth_token')
username = cfg.CONF.keystone_authtoken.admin_user
password = cfg.CONF.keystone_authtoken.admin_password
return v3.Password(username=username,
password=password,
user_domain_id='default',
auth_url=self._keystone_v3_endpoint,
trust_id=self.trust_id)
if self.auth_token_info:
auth_ref = access.AccessInfo.factory(body=self.auth_token_info,
auth_token=self.auth_token)
return _AccessInfoPlugin(self._keystone_v3_endpoint, auth_ref)
if self.auth_token:
# FIXME(jamielennox): This is broken but consistent. If you
# only have a token but don't load a service catalog then
# url_for wont work. Stub with the keystone endpoint so at
# least it might be right.
return token_endpoint.Token(endpoint=self._keystone_v3_endpoint,
token=self.auth_token)
if self.password:
return v3.Password(username=self.username,
password=self.password,
project_id=self.tenant_id,
user_domain_id='default',
auth_url=self._keystone_v3_endpoint)
LOG.error(_LE("Keystone v3 API connection failed, no password "
"trust or auth_token!"))
raise exception.AuthorizationFailure()
@property
def auth_plugin(self):
if not self._auth_plugin:
self._auth_plugin = self._create_auth_plugin()
return self._auth_plugin
def get_admin_context(show_deleted=False):
return RequestContext(is_admin=True, show_deleted=show_deleted)
@@ -160,6 +258,7 @@ class ContextMiddleware(wsgi.Middleware):
if roles is not None:
roles = roles.split(',')
token_info = environ.get('keystone.token_info')
auth_plugin = environ.get('keystone.token_auth')
req_id = environ.get(oslo_request_id.ENV_REQUEST_ID)
except Exception:
@@ -175,7 +274,8 @@ class ContextMiddleware(wsgi.Middleware):
roles=roles,
request_id=req_id,
auth_token_info=token_info,
region_name=region_name)
region_name=region_name,
auth_plugin=auth_plugin)
def ContextMiddleware_filter_factory(global_conf, **local_conf):

View File

@@ -14,11 +14,9 @@
"""Keystone Client functionality for use by resources."""
import collections
import copy
import json
import uuid
from keystoneclient.auth.identity import v3 as kc_auth_v3
import keystoneclient.exceptions as kc_exception
from keystoneclient import session
from keystoneclient.v3 import client as kc_v3
@@ -75,6 +73,8 @@ class KeystoneClientV3(object):
self._admin_client = None
self._domain_admin_client = None
self.session = session.Session.construct(self._ssl_options())
if self.context.auth_url:
self.v3_endpoint = self.context.auth_url.replace('v2.0', 'v3')
else:
@@ -158,66 +158,25 @@ class KeystoneClientV3(object):
return self._domain_admin_client
def _v3_client_init(self):
kwargs = {
'auth_url': self.v3_endpoint,
'endpoint': self.v3_endpoint
}
# Note try trust_id first, as we can't reuse auth_token in that case
if self.context.trust_id is not None:
# We got a trust_id, so we use the admin credentials
# to authenticate with the trust_id so we can use the
# trust impersonating the trustor user.
kwargs.update(self._service_admin_creds())
kwargs['trust_id'] = self.context.trust_id
kwargs.pop('project_name')
elif self.context.auth_token_info is not None:
# The auth_ref version must be set according to the token version
if 'access' in self.context.auth_token_info:
kwargs['auth_ref'] = copy.deepcopy(
self.context.auth_token_info['access'])
kwargs['auth_ref']['version'] = 'v2.0'
kwargs['auth_ref']['token']['id'] = self.context.auth_token
elif 'token' in self.context.auth_token_info:
kwargs['auth_ref'] = copy.deepcopy(
self.context.auth_token_info['token'])
kwargs['auth_ref']['version'] = 'v3'
kwargs['auth_ref']['auth_token'] = self.context.auth_token
else:
LOG.error(_LE('Unknown version in auth_token_info'))
raise exception.AuthorizationFailure(
_('Unknown token version'))
elif self.context.auth_token is not None:
kwargs['token'] = self.context.auth_token
kwargs['project_id'] = self.context.tenant_id
elif self.context.password is not None:
kwargs['username'] = self.context.username
kwargs['password'] = self.context.password
kwargs['project_id'] = self.context.tenant_id
else:
LOG.error(_LE("Keystone v3 API connection failed, no password "
"trust or auth_token!"))
raise exception.AuthorizationFailure()
kwargs.update(self._ssl_options())
client = kc_v3.Client(**kwargs)
client = kc_v3.Client(session=self.session,
auth=self.context.auth_plugin)
# If auth_ref has already be specified via auth_token_info, don't
# authenticate as we want to reuse, rather than request a new token
if 'auth_ref' not in kwargs:
if hasattr(self.context.auth_plugin, 'get_access'):
# NOTE(jamielennox): get_access returns the current token without
# reauthenticating if it's present and valid.
try:
client.authenticate()
auth_ref = self.context.auth_plugin.get_access(self.session)
except kc_exception.Unauthorized:
LOG.error(_LE("Keystone client authentication failed"))
raise exception.AuthorizationFailure()
# If we are authenticating with a trust set the context auth_token
# with the trust scoped token
if 'trust_id' in kwargs:
if self.context.trust_id:
# Sanity check
if not client.auth_ref.trust_scoped:
if not auth_ref.trust_scoped:
LOG.error(_LE("trust token re-scoping failed!"))
raise exception.AuthorizationFailure()
# Sanity check that impersonation is effective
if self.context.trustor_user_id != client.auth_ref.user_id:
if self.context.trustor_user_id != auth_ref.user_id:
LOG.error(_LE("Trust impersonation failed"))
raise exception.AuthorizationFailure()
@@ -282,29 +241,29 @@ class KeystoneClientV3(object):
# can't lookup the ID in keystoneclient unless they're admin
# workaround this by getting the user_id from admin_client
trustee_user_id = self.admin_client.auth_ref.user_id
trustor_user_id = self.client.auth_ref.user_id
trustor_project_id = self.client.auth_ref.project_id
trustor = self.context.auth_plugin.get_access(self.session)
# inherit the roles of the trustor, unless set trusts_delegated_roles
if cfg.CONF.trusts_delegated_roles:
roles = cfg.CONF.trusts_delegated_roles
else:
roles = self.context.roles
try:
trust = self.client.trusts.create(trustor_user=trustor_user_id,
trust = self.client.trusts.create(trustor_user=trustor.user_id,
trustee_user=trustee_user_id,
project=trustor_project_id,
project=trustor.project_id,
impersonation=True,
role_names=roles)
except kc_exception.NotFound:
LOG.debug("Failed to find roles %s for user %s"
% (roles, trustor_user_id))
% (roles, trustor.user_id))
raise exception.MissingCredentialError(
required=_("roles %s") % roles)
trust_context = context.RequestContext.from_dict(
self.context.to_dict())
trust_context.trust_id = trust.id
trust_context.trustor_user_id = trustor_user_id
trust_context.trustor_user_id = trustor.user_id
return trust_context
def delete_trust(self, trust_id):
@@ -383,19 +342,6 @@ class KeystoneClientV3(object):
# catalog (the token is expected to be used inside an instance
# where a specific endpoint will be specified, and user-data
# space is limited..)
if self._stack_domain_is_id:
auth = kc_auth_v3.Password(auth_url=self.v3_endpoint,
user_id=user_id,
password=password,
project_id=project_id,
user_domain_id=self.stack_domain)
else:
auth = kc_auth_v3.Password(auth_url=self.v3_endpoint,
user_id=user_id,
password=password,
project_id=project_id,
user_domain_name=self.stack_domain)
sess = session.Session(auth=auth)
# Note we do this directly via a post as there's currently
# no way to get a nocatalog token via keystoneclient
token_url = "%s/auth/tokens?nocatalog" % self.v3_endpoint
@@ -410,8 +356,8 @@ class KeystoneClientV3(object):
'domain': domain,
'password': password, 'id': user_id}},
'methods': ['password']}}}
t = sess.post(token_url, headers=headers, json=body,
authenticated=False)
t = self.session.post(token_url, headers=headers,
json=body, authenticated=False)
return t.headers['X-Subject-Token']
def create_stack_domain_user(self, username, project_id, password=None):
@@ -589,7 +535,7 @@ class KeystoneClientV3(object):
raise ValueError("Must specify either credential_id or access")
def create_ec2_keypair(self, user_id=None):
user_id = user_id or self.client.auth_ref.user_id
user_id = user_id or self.context.get_access(self.session).user_id
project_id = self.context.tenant_id
data_blob = {'access': uuid.uuid4().hex,
'secret': uuid.uuid4().hex}
@@ -665,11 +611,11 @@ class KeystoneClientV3(object):
default_region_name = (self.context.region_name or
cfg.CONF.region_name_for_services)
kwargs.setdefault('region_name', default_region_name)
return self.client.service_catalog.url_for(**kwargs)
return self.context.auth_plugin.get_endpoint(self.session, **kwargs)
@property
def auth_token(self):
return self.client.auth_token
return self.context.auth_plugin.get_token(self.session)
class KeystoneClient(object):

View File

@@ -13,6 +13,7 @@
import abc
from keystoneclient import exceptions
from oslo.config import cfg
import six
@@ -41,14 +42,34 @@ class ClientPlugin(object):
@property
def auth_token(self):
# Always use the auth_token from the keystone client, as
# this may be refreshed if the context contains credentials
# which allow reissuing of a new token before the context
# auth_token expiry (e.g trust_id or username/password)
return self.clients.client('keystone').auth_token
# NOTE(jamielennox): use the session defined by the keystoneclient
# options as traditionally the token was always retrieved from
# keystoneclient.
session = self.clients.client('keystone').session
return self.context.auth_plugin.get_token(session)
def url_for(self, **kwargs):
return self.clients.client('keystone').url_for(**kwargs)
# NOTE(jamielennox): use the session defined by the keystoneclient
# options as traditionally the token was always retrieved from
# keystoneclient.
session = self.clients.client('keystone').session
try:
kwargs.setdefault('interface', kwargs.pop('endpoint_type'))
except KeyError:
pass
reg = self.context.region_name or cfg.CONF.region_name_for_services
kwargs.setdefault('region_name', reg)
url = self.context.auth_plugin.get_endpoint(session, **kwargs)
# NOTE(jamielennox): raising exception maintains compatibility with
# older keystoneclient service catalog searching.
if url is None:
raise exceptions.EndpointNotFound()
return url
def _get_client_option(self, client, option):
# look for the option in the [clients_${client}] section

View File

@@ -24,6 +24,7 @@ from oslotest import mockpatch
import testscenarios
import testtools
from heat.common import context
from heat.common import messaging
from heat.engine.clients.os import cinder
from heat.engine.clients.os import glance
@@ -137,6 +138,13 @@ class HeatTestCase(testscenarios.WithScenarios,
mockfixture = self.useFixture(mockpatch.Patch(target, **kwargs))
return mockfixture.mock
def stub_auth(self, ctx=None, **kwargs):
auth = self.patchobject(ctx or context.RequestContext,
"_create_auth_plugin")
fake_auth = fakes.FakeAuth(**kwargs)
auth.return_value = fake_auth
return auth
def stub_keystoneclient(self, fake_client=None, **kwargs):
client = self.patchobject(keystone.KeystoneClientPlugin, "_create")
fkc = fake_client or fakes.FakeKeystoneClient(**kwargs)

View File

@@ -19,7 +19,8 @@ wrong the tests might raise AssertionError. I've indicated in comments the
places where actual behavior differs from the spec.
"""
from keystoneclient import exceptions
from keystoneclient import auth
from keystoneclient import session
from heat.common import context
@@ -77,18 +78,34 @@ class FakeClient(object):
pass
class FakeAuth(auth.BaseAuthPlugin):
def __init__(self, auth_token='abcd1234', only_services=None):
self.auth_token = auth_token
self.only_services = only_services
def get_token(self, session, **kwargs):
return self.auth_token
def get_endpoint(self, session, service_type=None, **kwargs):
if (self.only_services is not None and
service_type not in self.only_services):
return None
return 'http://example.com:1234/v1'
class FakeKeystoneClient(object):
def __init__(self, username='test_user', password='apassword',
user_id='1234', access='4567', secret='8901',
credential_id='abcdxyz', auth_token='abcd1234',
only_services=None):
credential_id='abcdxyz'):
self.username = username
self.password = password
self.user_id = user_id
self.access = access
self.secret = secret
self.session = session.Session()
self.credential_id = credential_id
self.only_services = only_services
class FakeCred(object):
id = self.credential_id
@@ -96,8 +113,6 @@ class FakeKeystoneClient(object):
secret = self.secret
self.creds = FakeCred()
self.auth_token = auth_token
def create_stack_user(self, username, password=''):
self.username = username
return self.user_id
@@ -131,15 +146,6 @@ class FakeKeystoneClient(object):
def disable_stack_user(self, user_id):
pass
def url_for(self, **kwargs):
if self.only_services is not None:
if ('service_type' in kwargs and
kwargs['service_type'] not in self.only_services):
# keystone client throws keystone exceptions, not cinder
# exceptions.
raise exceptions.EndpointNotFound()
return 'http://example.com:1234/v1'
def create_trust_context(self):
return context.RequestContext(username=self.username,
password=self.password,

View File

@@ -14,8 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from keystoneclient.auth.identity import v3 as ks_v3_auth
from keystoneclient import exceptions as keystone_exc
from keystoneclient.v3 import client as keystone_client
from keystoneclient import session as ks_session
import mox
from oslo.config import cfg
import webob
@@ -76,6 +78,7 @@ TOKEN_V3_RESPONSE = {
'id': 'tenant_id1',
'name': 'tenant_name1',
},
'methods': ['password'],
},
'user': {
'id': 'user_id1',
@@ -120,21 +123,20 @@ class KeystonePasswordAuthProtocolTest(common.HeatTestCase):
self.response_headers = dict(headers)
def test_valid_v2_request(self):
mock_client = self.m.CreateMock(keystone_client.Client)
self.m.StubOutWithMock(keystone_client, 'Client')
keystone_client.Client(
mock_auth = self.m.CreateMock(ks_v3_auth.Password)
self.m.StubOutWithMock(ks_v3_auth, 'Password')
ks_v3_auth.Password(
auth_url=self.config['auth_uri'],
cacert=None,
cert=None,
endpoint=self.config['auth_uri'],
insecure=False,
key=None,
password='goodpassword',
project_id='tenant_id1',
username='user_name1').AndReturn(mock_client)
mock_client.auth_ref = TOKEN_V2_RESPONSE
user_domain_id='default',
username='user_name1').AndReturn(mock_auth)
m = mock_auth.get_access(mox.IsA(ks_session.Session))
m.AndReturn(TOKEN_V2_RESPONSE)
self.app.expected_env['keystone.token_info'] = TOKEN_V2_RESPONSE
mock_client.authenticate().AndReturn(None)
self.m.ReplayAll()
req = webob.Request.blank('/tenant_id1/')
req.headers['X_AUTH_USER'] = 'user_name1'
@@ -144,23 +146,21 @@ class KeystonePasswordAuthProtocolTest(common.HeatTestCase):
self.m.VerifyAll()
def test_valid_v3_request(self):
mock_client = self.m.CreateMock(keystone_client.Client)
self.m.StubOutWithMock(keystone_client, 'Client')
keystone_client.Client(
auth_url=self.config['auth_uri'],
cacert=None,
cert=None,
endpoint=self.config['auth_uri'],
insecure=False,
key=None,
mock_auth = self.m.CreateMock(ks_v3_auth.Password)
self.m.StubOutWithMock(ks_v3_auth, 'Password')
ks_v3_auth.Password(auth_url=self.config['auth_uri'],
password='goodpassword',
project_id='tenant_id1',
username='user_name1').AndReturn(mock_client)
mock_client.auth_ref = TOKEN_V3_RESPONSE
user_domain_id='default',
username='user_name1').AndReturn(mock_auth)
m = mock_auth.get_access(mox.IsA(ks_session.Session))
m.AndReturn(TOKEN_V3_RESPONSE)
self.app.expected_env['keystone.token_info'] = {
'token': TOKEN_V3_RESPONSE
}
mock_client.authenticate().AndReturn(None)
self.m.ReplayAll()
req = webob.Request.blank('/tenant_id1/')
req.headers['X_AUTH_USER'] = 'user_name1'
@@ -170,18 +170,15 @@ class KeystonePasswordAuthProtocolTest(common.HeatTestCase):
self.m.VerifyAll()
def test_request_with_bad_credentials(self):
self.m.StubOutWithMock(
keystone_client, 'Client', use_mock_anything=True)
keystone_client.Client(
auth_url=self.config['auth_uri'],
cacert=None,
cert=None,
endpoint=self.config['auth_uri'],
insecure=False,
key=None,
self.m.StubOutWithMock(ks_v3_auth, 'Password')
m = ks_v3_auth.Password(auth_url=self.config['auth_uri'],
password='badpassword',
project_id='tenant_id1',
username='user_name1').AndRaise(keystone_exc.Unauthorized(401))
user_domain_id='default',
username='user_name1')
m.AndRaise(keystone_exc.Unauthorized(401))
self.m.ReplayAll()
req = webob.Request.blank('/tenant_id1/')
req.headers['X_AUTH_USER'] = 'user_name1'

View File

@@ -31,6 +31,7 @@ from heat.common import exception
from heat.engine import clients
from heat.engine.clients import client_plugin
from heat.tests import common
from heat.tests import fakes
from heat.tests import utils
from heat.tests.v1_1 import fakes as fakes_v1_1
@@ -87,11 +88,11 @@ class ClientsTest(common.HeatTestCase):
@mock.patch.object(heatclient, 'Client')
def test_clients_heat_no_auth_token(self, mock_call):
self.stub_keystoneclient(auth_token='anewtoken')
con = mock.Mock()
con.auth_url = "http://auth.example.com:5000/v2.0"
con.tenant_id = "b363706f891f48019483f8bd6503c54b"
con.auth_token = None
con.auth_plugin = fakes.FakeAuth(auth_token='anewtoken')
c = clients.Clients(con)
con.clients = c
@@ -102,11 +103,12 @@ class ClientsTest(common.HeatTestCase):
@mock.patch.object(heatclient, 'Client')
def test_clients_heat_cached(self, mock_call):
self.stub_keystoneclient()
self.stub_auth()
con = mock.Mock()
con.auth_url = "http://auth.example.com:5000/v2.0"
con.tenant_id = "b363706f891f48019483f8bd6503c54b"
con.auth_token = "3bcc3d3a03f44e3d8377f9247b0ad155"
con.trust_id = None
c = clients.Clients(con)
con.clients = c
@@ -120,22 +122,6 @@ class ClientsTest(common.HeatTestCase):
heat_cached = obj.client()
self.assertEqual(heat, heat_cached)
def test_clients_auth_token_update(self):
fkc = self.stub_keystoneclient(auth_token='token1')
con = mock.Mock()
con.auth_url = "http://auth.example.com:5000/v2.0"
con.trust_id = "b363706f891f48019483f8bd6503c54b"
con.username = 'heat'
con.password = 'verysecret'
con.auth_token = None
obj = clients.Clients(con)
con.clients = obj
self.assertIsNotNone(obj.client('heat'))
self.assertEqual('token1', obj.auth_token)
fkc.auth_token = 'token2'
self.assertEqual('token2', obj.auth_token)
class FooClientsPlugin(client_plugin.ClientPlugin):
@@ -177,37 +163,37 @@ class ClientPluginTest(common.HeatTestCase):
def test_auth_token(self):
con = mock.Mock()
con.auth_token = "1234"
con.trust_id = None
c = clients.Clients(con)
con.clients = c
c.client = mock.Mock(name="client")
mock_keystone = mock.Mock()
c.client.return_value = mock_keystone
mock_keystone.auth_token = '5678'
con.auth_plugin = mock.Mock(name="auth_plugin")
con.auth_plugin.get_token = mock.Mock(name="get_token")
con.auth_plugin.get_token.return_value = '5678'
plugin = FooClientsPlugin(con)
# assert token is from keystone rather than context
# assert token is from plugin rather than context
# even though both are set
self.assertEqual('5678', plugin.auth_token)
c.client.assert_called_with('keystone')
con.auth_plugin.get_token.assert_called()
def test_url_for(self):
con = mock.Mock()
con.auth_token = "1234"
con.trust_id = None
c = clients.Clients(con)
con.clients = c
c.client = mock.Mock(name="client")
mock_keystone = mock.Mock()
c.client.return_value = mock_keystone
mock_keystone.url_for.return_value = 'http://192.0.2.1/foo'
con.auth_plugin = mock.Mock(name="auth_plugin")
con.auth_plugin.get_endpoint = mock.Mock(name="get_endpoint")
con.auth_plugin.get_endpoint.return_value = 'http://192.0.2.1/foo'
plugin = FooClientsPlugin(con)
self.assertEqual('http://192.0.2.1/foo',
plugin.url_for(service_type='foo'))
c.client.assert_called_with('keystone')
con.auth_plugin.get_endpoint.assert_called()
def test_abstract_create(self):
con = mock.Mock()
@@ -706,19 +692,19 @@ class TestIsNotFound(common.HeatTestCase):
class ClientAPIVersionTest(common.HeatTestCase):
def test_cinder_api_v1_and_v2(self):
self.stub_keystoneclient()
self.stub_auth()
ctx = utils.dummy_context()
client = clients.Clients(ctx).client('cinder')
self.assertEqual(2, client.volume_api_version)
def test_cinder_api_v1_only(self):
self.stub_keystoneclient(only_services=['volume'])
self.stub_auth(only_services=['volume'])
ctx = utils.dummy_context()
client = clients.Clients(ctx).client('cinder')
self.assertEqual(1, client.volume_api_version)
def test_cinder_api_v2_only(self):
self.stub_keystoneclient(only_services=['volumev2'])
self.stub_auth(only_services=['volumev2'])
ctx = utils.dummy_context()
client = clients.Clients(ctx).client('cinder')
self.assertEqual(2, client.volume_api_version)

View File

@@ -14,7 +14,9 @@
import json
import uuid
from keystoneclient import access as ks_access
from keystoneclient.auth.identity import v3 as ks_auth_v3
from keystoneclient.auth import token_endpoint as ks_token_endpoint
import keystoneclient.exceptions as kc_exception
from keystoneclient import session as ks_session
from keystoneclient.v3 import client as kc_v3
@@ -23,6 +25,7 @@ import mox
from oslo.config import cfg
import six
from heat.common import context
from heat.common import exception
from heat.common import heat_keystoneclient
from heat.tests import common
@@ -44,6 +47,9 @@ class KeystoneClientTest(common.HeatTestCase):
self.mock_ks_v3_client_domain_mngr = self.m.CreateMock(
kc_v3_domains.DomainManager)
self.m.StubOutWithMock(kc_v3, "Client")
self.m.StubOutWithMock(ks_auth_v3, 'Password')
self.m.StubOutWithMock(ks_token_endpoint, 'Token')
self.m.StubOutWithMock(context, '_AccessInfoPlugin')
dummy_url = 'http://server.test:5000/v2.0'
cfg.CONF.set_override('auth_uri', dummy_url,
@@ -100,56 +106,49 @@ class KeystoneClientTest(common.HeatTestCase):
self.mock_admin_client.auth_ref = self.m.CreateMockAnything()
self.mock_admin_client.auth_ref.user_id = '1234'
def _stubs_v3(self, method='token', auth_ok=True, trust_scoped=True,
user_id='trustor_user_id', auth_ref=None):
def _stubs_v3(self, method='token', trust_scoped=True,
user_id='trustor_user_id', auth_ref=None, client=True,
times=1):
mock_auth_ref = self.m.CreateMockAnything()
mock_ks_auth = self.m.CreateMockAnything()
if method == 'token':
kc_v3.Client(
token='abcd1234', project_id='test_tenant_id',
auth_url='http://server.test:5000/v3',
endpoint='http://server.test:5000/v3',
cacert=None,
cert=None,
insecure=False,
key=None).AndReturn(self.mock_ks_v3_client)
p = ks_token_endpoint.Token(token='abcd1234',
endpoint='http://server.test:5000/v3')
elif method == 'auth_ref':
kc_v3.Client(
auth_ref=auth_ref,
auth_url='http://server.test:5000/v3',
endpoint='http://server.test:5000/v3',
cacert=None,
cert=None,
insecure=False,
key=None).AndReturn(self.mock_ks_v3_client)
p = context._AccessInfoPlugin('http://server.test:5000/v3',
mox.IsA(ks_access.AccessInfo))
elif method == 'password':
kc_v3.Client(
p = ks_auth_v3.Password(auth_url='http://server.test:5000/v3',
username='test_username',
password='password',
project_id='test_tenant_id',
auth_url='http://server.test:5000/v3',
endpoint='http://server.test:5000/v3',
cacert=None,
cert=None,
insecure=False,
key=None).AndReturn(self.mock_ks_v3_client)
user_domain_id='default')
elif method == 'trust':
kc_v3.Client(
p = ks_auth_v3.Password(auth_url='http://server.test:5000/v3',
username='heat',
password='verybadpass',
auth_url='http://server.test:5000/v3',
endpoint='http://server.test:5000/v3',
trust_id='atrust123',
cacert=None,
cert=None,
insecure=False,
key=None).AndReturn(self.mock_ks_v3_client)
self.mock_ks_v3_client.auth_ref = self.m.CreateMockAnything()
self.mock_ks_v3_client.auth_ref.user_id = user_id
self.mock_ks_v3_client.auth_ref.trust_scoped = trust_scoped
self.mock_ks_v3_client.auth_ref.auth_token = 'atrusttoken'
user_domain_id='default',
trust_id='atrust123')
if method != 'auth_ref':
self.mock_ks_v3_client.authenticate().AndReturn(auth_ok)
mock_auth_ref.user_id = user_id
mock_auth_ref.trust_scoped = trust_scoped
mock_auth_ref.auth_token = 'atrusttoken'
p.AndReturn(mock_ks_auth)
if client:
c = kc_v3.Client(session=mox.IsA(ks_session.Session),
auth=mock_ks_auth)
c.AndReturn(self.mock_ks_v3_client)
for x in xrange(0, times):
m = mock_ks_auth.get_access(mox.IsA(ks_session.Session))
m.AndReturn(mock_auth_ref)
return mock_ks_auth, mock_auth_ref
def test_username_length(self):
"""Test that user names >64 characters are properly truncated."""
@@ -427,7 +426,8 @@ class KeystoneClientTest(common.HeatTestCase):
ctx.password = None
ctx.trust_id = None
ctx.auth_token = 'ctx_token'
ctx.auth_token_info = {'access': {'token': {'expires': '123'}}}
ctx.auth_token_info = {'access': {
'token': {'id': 'abcd1234', 'expires': '123'}}}
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.client
self.assertIsNotNone(heat_ks_client._client)
@@ -437,7 +437,9 @@ class KeystoneClientTest(common.HeatTestCase):
"""Test creating the client, token v3 auth_ref."""
expected_auth_ref = {'auth_token': 'ctx_token',
'expires': '456', 'version': 'v3'}
'expires': '456',
'version': 'v3',
'methods': []}
self._stubs_v3(method='auth_ref', auth_ref=expected_auth_ref)
self.m.ReplayAll()
@@ -446,7 +448,7 @@ class KeystoneClientTest(common.HeatTestCase):
ctx.password = None
ctx.trust_id = None
ctx.auth_token = 'ctx_token'
ctx.auth_token_info = {'token': {'expires': '456'}}
ctx.auth_token_info = {'token': {'expires': '456', 'methods': []}}
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.client
self.assertIsNotNone(heat_ks_client._client)
@@ -510,19 +512,19 @@ class KeystoneClientTest(common.HeatTestCase):
id = 'atrust123'
self._stub_admin_client()
mock_client, mock_auth_ref = self._stubs_v3(times=2)
self._stubs_v3()
cfg.CONF.set_override('deferred_auth_method', 'trusts')
if delegate_roles:
cfg.CONF.set_override('trusts_delegated_roles', delegate_roles)
trustor_roles = ['heat_stack_owner', 'admin', '__member__']
trustee_roles = delegate_roles or trustor_roles
self.mock_ks_v3_client.auth_ref = self.m.CreateMockAnything()
self.mock_ks_v3_client.auth_ref.user_id = '5678'
self.mock_ks_v3_client.auth_ref.project_id = '42'
self.mock_ks_v3_client.trusts = self.m.CreateMockAnything()
mock_auth_ref.user_id = '5678'
mock_auth_ref.project_id = '42'
self.mock_ks_v3_client.trusts = self.m.CreateMockAnything()
self.mock_ks_v3_client.trusts.create(
trustor_user='5678',
trustee_user='1234',
@@ -543,18 +545,15 @@ class KeystoneClientTest(common.HeatTestCase):
"""Test create_trust_context when creating a trust."""
class MockTrust(object):
id = 'atrust123'
self._stub_admin_client()
self._stubs_v3()
mock_auth, mock_auth_ref = self._stubs_v3(times=2)
cfg.CONF.set_override('deferred_auth_method', 'trusts')
cfg.CONF.set_override('trusts_delegated_roles', ['heat_stack_owner'])
self.mock_ks_v3_client.auth_ref = self.m.CreateMockAnything()
self.mock_ks_v3_client.auth_ref.user_id = '5678'
self.mock_ks_v3_client.auth_ref.project_id = '42'
mock_auth_ref.user_id = '5678'
mock_auth_ref.project_id = '42'
self.mock_ks_v3_client.trusts = self.m.CreateMockAnything()
self.mock_ks_v3_client.trusts.create(
trustor_user='5678',
@@ -1366,7 +1365,6 @@ class KeystoneClientTest(common.HeatTestCase):
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
def _stub_domain_user_pw_auth(self):
self.m.StubOutWithMock(ks_auth_v3, 'Password')
ks_auth_v3.Password(auth_url='http://server.test:5000/v3',
user_id='duser',
password='apassw',
@@ -1375,7 +1373,6 @@ class KeystoneClientTest(common.HeatTestCase):
def test_stack_domain_user_token(self):
"""Test stack_domain_user_token function."""
self._stub_domain_user_pw_auth()
dummysession = self.m.CreateMockAnything()
dummyresp = self.m.CreateMockAnything()
dummyresp.headers = {'X-Subject-Token': 'dummytoken'}
@@ -1384,7 +1381,7 @@ class KeystoneClientTest(common.HeatTestCase):
headers={'Accept': 'application/json'},
json=mox.IgnoreArg()).AndReturn(dummyresp)
self.m.StubOutWithMock(ks_session, 'Session')
ks_session.Session(auth='dummyauth').AndReturn(dummysession)
ks_session.Session.construct(mox.IsA(dict)).AndReturn(dummysession)
self.m.ReplayAll()
ctx = utils.dummy_context()
@@ -1421,9 +1418,8 @@ class KeystoneClientTest(common.HeatTestCase):
Helper function for testing url_for depending on different ways to
pass region name.
"""
self._stubs_v3()
self.mock_ks_v3_client.service_catalog = self.m.CreateMockAnything()
self.mock_ks_v3_client.service_catalog.url_for(
mock_ks_auth, mock_auth_ref = self._stubs_v3(client=False)
mock_ks_auth.get_endpoint(mox.IsA(ks_session.Session),
**expected_kwargs).AndReturn(service_url)
self.m.ReplayAll()
@@ -1525,7 +1521,6 @@ class KeystoneClientTestDomainName(KeystoneClientTest):
self.mock_admin_client.auth_ref.user_id = '1234'
def _stub_domain_user_pw_auth(self):
self.m.StubOutWithMock(ks_auth_v3, 'Password')
ks_auth_v3.Password(auth_url='http://server.test:5000/v3',
user_id='duser',
password='apassw',

View File

@@ -972,7 +972,7 @@ class StackTest(common.HeatTestCase):
def test_no_auth_token(self):
ctx = utils.dummy_context()
ctx.auth_token = None
self.stub_keystoneclient()
self.stub_auth()
self.m.ReplayAll()
stack = parser.Stack(ctx, 'test_stack', self.tmpl)

View File

@@ -77,7 +77,7 @@ class swiftTest(common.HeatTestCase):
self.m.StubOutWithMock(sc.Connection, 'delete_object')
self.m.StubOutWithMock(sc.Connection, 'head_container')
self.m.StubOutWithMock(sc.Connection, 'get_auth')
self.stub_keystoneclient()
self.stub_auth()
def create_resource(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)