From 9cdf7d52bf07cb528b669cc104d1a9a3b800d985 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Thu, 4 Jul 2013 11:50:49 +1200 Subject: [PATCH] Enforce credentials requirement on stack create/update. Credentials need to be supplied whenever they are stored with the stack, which is during stack create and update. Some users have been relying on their auth token to perform all heat operations (as all other openstack services support) however this has led them to experience obscure errors when wait handles or alarms are triggered. Failing early with a clear error message will stop this class of errors. Fixes bug: #1194303 Change-Id: I3edef3dee843bb06760be6294b798761eba30cb8 --- heat/api/openstack/v1/util.py | 1 + heat/engine/service.py | 10 ++++++++ heat/tests/test_engine_service.py | 40 +++++++++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py index 76920868..b578e1cb 100644 --- a/heat/api/openstack/v1/util.py +++ b/heat/api/openstack/v1/util.py @@ -85,6 +85,7 @@ def remote_error(ex): 'StackValidationFailed': exc.HTTPBadRequest, 'InvalidTemplateReference': exc.HTTPBadRequest, 'UnknownUserParameter': exc.HTTPBadRequest, + 'MissingCredentialError': exc.HTTPBadRequest, } Exc = error_map.get(ex.exc_type, exc.HTTPInternalServerError) diff --git a/heat/engine/service.py b/heat/engine/service.py index 5abb939f..81d8e1a4 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -184,6 +184,12 @@ class EngineService(service.Service): stacks = db_api.stack_get_all_by_tenant(cnxt) or [] return list(format_stack_details(stacks)) + def _validate_mandatory_credentials(self, cnxt): + if cnxt.username is None: + raise exception.MissingCredentialError(required='X-Auth-User') + if cnxt.password is None: + raise exception.MissingCredentialError(required='X-Auth-Key') + @request_context def create_stack(self, cnxt, stack_name, template, params, files, args): """ @@ -201,6 +207,8 @@ class EngineService(service.Service): """ logger.info('template is %s' % template) + self._validate_mandatory_credentials(cnxt) + def _stack_create(stack): # Create the stack, and create the periodic task if successful stack.create() @@ -246,6 +254,8 @@ class EngineService(service.Service): """ logger.info('template is %s' % template) + self._validate_mandatory_credentials(cnxt) + # Get the database representation of the existing stack db_stack = self._get_stack(cnxt, stack_identity) diff --git a/heat/tests/test_engine_service.py b/heat/tests/test_engine_service.py index b524aed2..c696a46c 100644 --- a/heat/tests/test_engine_service.py +++ b/heat/tests/test_engine_service.py @@ -67,12 +67,14 @@ wp_template = ''' def create_context(mocks, user='stacks_test_user', - tenant='test_admin', ctx=None): - ctx = ctx or context.get_admin_context() + tenant='test_admin', password='stacks_test_password'): + ctx = context.get_admin_context() mocks.StubOutWithMock(ctx, 'username') mocks.StubOutWithMock(ctx, 'tenant_id') + mocks.StubOutWithMock(ctx, 'password') ctx.username = user ctx.tenant_id = tenant + ctx.password = password return ctx @@ -318,6 +320,21 @@ class stackServiceCreateUpdateDeleteTest(HeatTestCase): self.ctx, stack_name, stack.t, {}, None, {}) + def test_stack_create_no_credentials(self): + stack_name = 'service_create_test_stack' + params = {'foo': 'bar'} + template = '{ "Template": "data" }' + + ctx = self.ctx = create_context(self.m, password=None) + self.assertRaises(exception.MissingCredentialError, + self.man.create_stack, ctx, stack_name, template, + params, None, {}) + + ctx = self.ctx = create_context(self.m, user=None) + self.assertRaises(exception.MissingCredentialError, + self.man.create_stack, ctx, stack_name, template, + params, None, {}) + def test_stack_validate(self): stack_name = 'service_create_test_validate' stack = get_wordpress_stack(stack_name, self.ctx) @@ -464,6 +481,25 @@ class stackServiceCreateUpdateDeleteTest(HeatTestCase): None, {}) self.m.VerifyAll() + def test_stack_update_no_credentials(self): + stack_name = 'service_update_nonexist_test_stack' + params = {'foo': 'bar'} + template = '{ "Template": "data" }' + + stack = get_wordpress_stack(stack_name, self.ctx) + + ctx = self.ctx = create_context(self.m, password=None) + self.assertRaises(exception.MissingCredentialError, + self.man.update_stack, + ctx, stack.identifier(), template, params, + None, {}) + + ctx = self.ctx = create_context(self.m, user=None) + self.assertRaises(exception.MissingCredentialError, + self.man.update_stack, + ctx, stack.identifier(), template, params, + None, {}) + class stackServiceSuspendResumeTest(HeatTestCase): -- 2.45.2