Factor out authenticate method for engine.
This patch factors out an authenticate() function for use by both the heat service authentication and the resource authentication. This fixes the AWS auth method for creating resources - issue #153. Change-Id: I134e993263ae6ba4890f56bfbe6a6a3205b7f921 Signed-off-by: Ian Main <imain@redhat.com>
This commit is contained in:
parent
38d382ad07
commit
e0c3141253
81
heat/engine/auth.py
Normal file
81
heat/engine/auth.py
Normal file
@ -0,0 +1,81 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
#
|
||||
# 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 json
|
||||
import httplib
|
||||
import urlparse
|
||||
from novaclient.v1_1 import client
|
||||
from novaclient.exceptions import BadRequest
|
||||
from novaclient.exceptions import NotFound
|
||||
from novaclient.exceptions import AuthorizationFailure
|
||||
from heat.common import context
|
||||
|
||||
logger = logging.getLogger('heat.engine.auth')
|
||||
|
||||
|
||||
def authenticate(con, service_type='heat', service_name='heat'):
|
||||
""" Authenticate a user context. This authenticates either an
|
||||
EC2 style key context or a keystone user/pass context.
|
||||
|
||||
In the case of EC2 style authentication this will also set the
|
||||
username in the context so we can use it to key in the database.
|
||||
"""
|
||||
|
||||
if con.password is not None:
|
||||
nova = client.Client(con.username, con.password,
|
||||
con.tenant, con.auth_url,
|
||||
service_type=service_type,
|
||||
service_name=service_name)
|
||||
nova.authenticate()
|
||||
return nova
|
||||
else:
|
||||
# We'll have to do AWS style auth which is more complex.
|
||||
# First step is to get a token from the AWS creds.
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
o = urlparse.urlparse(con.aws_auth_uri)
|
||||
if o.scheme == 'http':
|
||||
conn = httplib.HTTPConnection(o.netloc)
|
||||
else:
|
||||
conn = httplib.HTTPSConnection(o.netloc)
|
||||
conn.request('POST', o.path, body=con.aws_creds, headers=headers)
|
||||
response = conn.getresponse().read()
|
||||
conn.close()
|
||||
|
||||
result = json.loads(response)
|
||||
try:
|
||||
token_id = result['access']['token']['id']
|
||||
# We grab the username here because with token auth and EC2
|
||||
# we never get it normally. We could pass it in but then We
|
||||
# are relying on user input to give us the correct username.
|
||||
# This one is the result of the authentication and is verified.
|
||||
username = result['access']['user']['username']
|
||||
con.username = username
|
||||
|
||||
logger.info("AWS authentication successful.")
|
||||
except (AttributeError, KeyError):
|
||||
# FIXME: Should be 404 I think.
|
||||
logger.info("AWS authentication failure.")
|
||||
raise exception.AuthorizationFailure()
|
||||
|
||||
nova = client.Client(con.service_user, con.service_password,
|
||||
con.tenant, con.auth_url,
|
||||
proxy_token=token_id,
|
||||
proxy_tenant_id=con.tenant_id,
|
||||
service_type=service_type,
|
||||
service_name=service_name)
|
||||
nova.authenticate()
|
||||
return nova
|
@ -32,6 +32,7 @@ from heat.common import context as ctxtlib
|
||||
from heat.engine import parser
|
||||
from heat.engine import resources
|
||||
from heat.engine import watchrule
|
||||
from heat.engine import auth
|
||||
from heat.openstack.common import timeutils
|
||||
|
||||
from novaclient.v1_1 import client
|
||||
@ -77,60 +78,6 @@ class EngineManager(manager.Manager):
|
||||
"""Load configuration options and connect to the hypervisor."""
|
||||
pass
|
||||
|
||||
def _authenticate(self, con):
|
||||
""" Authenticate against the 'heat' service. This should be
|
||||
the first call made in an endpoint call. I like to see this
|
||||
done explicitly so that it is clear there is an authentication
|
||||
request at the entry to the call.
|
||||
|
||||
In the case of EC2 style authentication this will also set the
|
||||
username in the context so we can use it to key in the database.
|
||||
"""
|
||||
|
||||
if con.password is not None:
|
||||
nova = client.Client(con.username, con.password,
|
||||
con.tenant, con.auth_url,
|
||||
service_type='heat',
|
||||
service_name='heat')
|
||||
nova.authenticate()
|
||||
else:
|
||||
# We'll have to do AWS style auth which is more complex.
|
||||
# First step is to get a token from the AWS creds.
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
o = urlparse.urlparse(con.aws_auth_uri)
|
||||
if o.scheme == 'http':
|
||||
conn = httplib.HTTPConnection(o.netloc)
|
||||
else:
|
||||
conn = httplib.HTTPSConnection(o.netloc)
|
||||
conn.request('POST', o.path, body=con.aws_creds, headers=headers)
|
||||
response = conn.getresponse().read()
|
||||
conn.close()
|
||||
|
||||
result = json.loads(response)
|
||||
try:
|
||||
token_id = result['access']['token']['id']
|
||||
# We grab the username here because with token auth and EC2
|
||||
# we never get it normally. We could pass it in but then We
|
||||
# are relying on user input to give us the correct username.
|
||||
# This one is the result of the authentication and is verified.
|
||||
username = result['access']['user']['username']
|
||||
con.username = username
|
||||
|
||||
logger.info("AWS authentication successful.")
|
||||
except (AttributeError, KeyError):
|
||||
# FIXME: Should be 404 I think.
|
||||
logger.info("AWS authentication failure.")
|
||||
raise exception.AuthorizationFailure()
|
||||
|
||||
nova = client.Client(con.service_user, con.service_password,
|
||||
con.tenant, con.auth_url,
|
||||
proxy_token=token_id,
|
||||
proxy_tenant_id=con.tenant_id,
|
||||
service_type='heat',
|
||||
service_name='heat')
|
||||
nova.authenticate()
|
||||
|
||||
def list_stacks(self, context, params):
|
||||
"""
|
||||
The list_stacks method is the end point that actually implements
|
||||
@ -139,7 +86,7 @@ class EngineManager(manager.Manager):
|
||||
arg2 -> Dict of http request parameters passed in from API side.
|
||||
"""
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
res = {'stacks': []}
|
||||
stacks = db_api.stack_get_by_user(context)
|
||||
@ -167,7 +114,7 @@ class EngineManager(manager.Manager):
|
||||
arg2 -> Name of the stack you want to see.
|
||||
arg3 -> Dict of http request parameters passed in from API side.
|
||||
"""
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
res = {'stacks': []}
|
||||
s = db_api.stack_get_by_name(context, stack_name)
|
||||
@ -209,7 +156,7 @@ class EngineManager(manager.Manager):
|
||||
"""
|
||||
logger.info('template is %s' % template)
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
if db_api.stack_get_by_name(None, stack_name):
|
||||
return {'Error': 'Stack already exists with that name.'}
|
||||
@ -267,7 +214,7 @@ class EngineManager(manager.Manager):
|
||||
arg4 -> Params passed from API.
|
||||
"""
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
logger.info('validate_template')
|
||||
if template is None:
|
||||
@ -297,7 +244,7 @@ class EngineManager(manager.Manager):
|
||||
arg2 -> Name of the stack you want to see.
|
||||
arg3 -> Dict of http request parameters passed in from API side.
|
||||
"""
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
s = db_api.stack_get_by_name(context, stack_name)
|
||||
if s:
|
||||
return s.raw_template.template
|
||||
@ -311,7 +258,7 @@ class EngineManager(manager.Manager):
|
||||
arg3 -> Params passed from API.
|
||||
"""
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
st = db_api.stack_get_by_name(context, stack_name)
|
||||
if not st:
|
||||
@ -347,7 +294,7 @@ class EngineManager(manager.Manager):
|
||||
arg3 -> Params passed from API.
|
||||
"""
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
if stack_name is not None:
|
||||
st = db_api.stack_get_by_name(context, stack_name)
|
||||
@ -362,7 +309,7 @@ class EngineManager(manager.Manager):
|
||||
|
||||
def event_create(self, context, event):
|
||||
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
stack_name = event['stack']
|
||||
resource_name = event['resource']
|
||||
@ -391,7 +338,7 @@ class EngineManager(manager.Manager):
|
||||
return [msg, None]
|
||||
|
||||
def describe_stack_resource(self, context, stack_name, resource_name):
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
stack = db_api.stack_get_by_name(context, stack_name)
|
||||
if not stack:
|
||||
@ -405,7 +352,7 @@ class EngineManager(manager.Manager):
|
||||
|
||||
def describe_stack_resources(self, context, stack_name,
|
||||
physical_resource_id, logical_resource_id):
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
if stack_name:
|
||||
stack = db_api.stack_get_by_name(context, stack_name)
|
||||
@ -433,7 +380,7 @@ class EngineManager(manager.Manager):
|
||||
return resources
|
||||
|
||||
def list_stack_resources(self, context, stack_name):
|
||||
self._authenticate(context)
|
||||
auth.authenticate(context)
|
||||
|
||||
stack = db_api.stack_get_by_name(context, stack_name)
|
||||
if not stack:
|
||||
|
@ -24,6 +24,7 @@ from heat.common import exception
|
||||
from heat.common.config import HeatEngineConfigOpts
|
||||
from heat.db import api as db_api
|
||||
from heat.engine import checkeddict
|
||||
from heat.engine import auth
|
||||
|
||||
logger = logging.getLogger('heat.engine.resources')
|
||||
|
||||
@ -121,13 +122,9 @@ class Resource(object):
|
||||
return self._nova[service_type]
|
||||
|
||||
con = self.stack.context
|
||||
self._nova[service_type] = nc.Client(con.username,
|
||||
con.password,
|
||||
con.tenant,
|
||||
con.auth_url,
|
||||
proxy_token=con.auth_token,
|
||||
proxy_tenant_id=con.tenant_id,
|
||||
service_type=service_type)
|
||||
self._nova[service_type] = auth.authenticate(con,
|
||||
service_type=service_type,
|
||||
service_name=None)
|
||||
return self._nova[service_type]
|
||||
|
||||
def calculate_properties(self):
|
||||
|
@ -15,6 +15,7 @@ from heat.engine import instance as instances
|
||||
import heat.db as db_api
|
||||
from heat.engine import parser
|
||||
from heat.engine import manager
|
||||
from heat.engine import auth
|
||||
|
||||
|
||||
@attr(tag=['unit', 'resource'])
|
||||
@ -149,8 +150,8 @@ class stacksTest(unittest.TestCase):
|
||||
ctx = context.get_admin_context()
|
||||
self.m.StubOutWithMock(ctx, 'username')
|
||||
ctx.username = 'fred'
|
||||
self.m.StubOutWithMock(manager.EngineManager, '_authenticate')
|
||||
manager.EngineManager._authenticate(ctx).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(ctx).AndReturn(True)
|
||||
|
||||
s = {}
|
||||
s['name'] = stack.name
|
||||
|
@ -16,6 +16,7 @@ from heat.engine import volume as volumes
|
||||
from heat.engine import manager as managers
|
||||
import heat.db as db_api
|
||||
from heat.engine import parser
|
||||
from heat.engine import auth
|
||||
|
||||
test_template_volumeattach = '''
|
||||
{
|
||||
@ -212,8 +213,8 @@ class validateTest(unittest.TestCase):
|
||||
|
||||
def test_validate_volumeattach_valid(self):
|
||||
t = json.loads(test_template_volumeattach % 'vdq')
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
params = {}
|
||||
stack = parser.Stack(None, 'test_stack', t, 0, params)
|
||||
|
||||
@ -227,8 +228,8 @@ class validateTest(unittest.TestCase):
|
||||
|
||||
def test_validate_volumeattach_invalid(self):
|
||||
t = json.loads(test_template_volumeattach % 'sda')
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
params = {}
|
||||
stack = parser.Stack(None, 'test_stack', t, 0, params)
|
||||
|
||||
@ -244,8 +245,8 @@ class validateTest(unittest.TestCase):
|
||||
t = json.loads(test_template_ref % 'WikiDatabase')
|
||||
t['Parameters']['KeyName']['Value'] = 'test'
|
||||
params = {}
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
|
||||
self.m.StubOutWithMock(instances.Instance, 'nova')
|
||||
instances.Instance.nova().AndReturn(self.fc)
|
||||
@ -261,8 +262,8 @@ class validateTest(unittest.TestCase):
|
||||
t = json.loads(test_template_ref % 'WikiDatabasez')
|
||||
t['Parameters']['KeyName']['Value'] = 'test'
|
||||
params = {}
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
|
||||
self.m.StubOutWithMock(instances.Instance, 'nova')
|
||||
instances.Instance.nova().AndReturn(self.fc)
|
||||
@ -277,8 +278,8 @@ class validateTest(unittest.TestCase):
|
||||
t = json.loads(test_template_findinmap_valid)
|
||||
t['Parameters']['KeyName']['Value'] = 'test'
|
||||
params = {}
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
|
||||
self.m.StubOutWithMock(instances.Instance, 'nova')
|
||||
instances.Instance.nova().AndReturn(self.fc)
|
||||
@ -293,8 +294,8 @@ class validateTest(unittest.TestCase):
|
||||
t = json.loads(test_template_findinmap_invalid)
|
||||
t['Parameters']['KeyName']['Value'] = 'test'
|
||||
params = {}
|
||||
self.m.StubOutWithMock(managers.EngineManager, '_authenticate')
|
||||
managers.EngineManager._authenticate(None).AndReturn(True)
|
||||
self.m.StubOutWithMock(auth, 'authenticate')
|
||||
auth.authenticate(None).AndReturn(True)
|
||||
|
||||
self.m.StubOutWithMock(instances.Instance, 'nova')
|
||||
instances.Instance.nova().AndReturn(self.fc)
|
||||
|
Loading…
Reference in New Issue
Block a user