]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Factor out authenticate method for engine.
authorIan Main <imain@redhat.com>
Fri, 22 Jun 2012 15:48:23 +0000 (08:48 -0700)
committerIan Main <imain@redhat.com>
Fri, 22 Jun 2012 17:58:54 +0000 (10:58 -0700)
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>
heat/engine/auth.py [new file with mode: 0644]
heat/engine/manager.py
heat/engine/resources.py
heat/tests/test_stacks.py
heat/tests/test_validate.py

diff --git a/heat/engine/auth.py b/heat/engine/auth.py
new file mode 100644 (file)
index 0000000..c8b27cf
--- /dev/null
@@ -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
index 0fee2e77ef8e6a8f4234655f4772601e7944d1f5..fd8b44a5e1a9f798efdd67a161d5d91bc8986a64 100644 (file)
@@ -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:
index c9072a1aa960eca1a0dd87a3eacab4f8ad5649f1..b577b80279c0978d57d597c7a9455371440cc53b 100644 (file)
@@ -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):
index 1a785467f5f39a7792b3a740e5962610f3cf3bc1..7daeb815fa83a8955a4eb239dfddb1805dc27668 100644 (file)
@@ -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
index a073935ac8097626297fede6201af6439678a8e9..94557017711f6e5c88ac6656985d5eba39c17e4d 100644 (file)
@@ -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)