diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py index 525716a212..56ff639cf0 100644 --- a/heat/db/sqlalchemy/api.py +++ b/heat/db/sqlalchemy/api.py @@ -269,8 +269,11 @@ def stack_get(context, stack_id, show_deleted=False, tenant_safe=True): if result is None or result.deleted_at is not None and not deleted_ok: return None + # One exception to normal project scoping is users created by the + # stacks in the stack_user_project_id (in the heat stack user domain) if (tenant_safe and result is not None and context is not None and - result.tenant != context.tenant_id): + context.tenant_id not in (result.tenant, + result.stack_user_project_id)): return None return result diff --git a/heat/engine/service.py b/heat/engine/service.py index 2386338d37..07827df235 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -263,16 +263,17 @@ class EngineService(service.Service): def _get_stack(self, cnxt, stack_identity, show_deleted=False): identity = identifier.HeatIdentifier(**stack_identity) - if identity.tenant != cnxt.tenant_id: - raise exception.InvalidTenant(target=identity.tenant, - actual=cnxt.tenant_id) - s = db_api.stack_get(cnxt, identity.stack_id, show_deleted=show_deleted) if s is None: raise exception.StackNotFound(stack_name=identity.stack_name) + if cnxt.tenant_id not in (identity.tenant, s.stack_user_project_id): + # The DB API should not allow this, but sanity-check anyway.. + raise exception.InvalidTenant(target=identity.tenant, + actual=cnxt.tenant_id) + if identity.path or s.name != identity.stack_name: raise exception.StackNotFound(stack_name=identity.stack_name) diff --git a/heat/tests/test_sqlalchemy_api.py b/heat/tests/test_sqlalchemy_api.py index 5f4d33e9a3..b6a7110071 100644 --- a/heat/tests/test_sqlalchemy_api.py +++ b/heat/tests/test_sqlalchemy_api.py @@ -1001,6 +1001,15 @@ class DBAPIStackTest(HeatTestCase): stack = db_api.stack_get(self.ctx, UUID1, show_deleted=False) self.assertIsNone(stack) + def test_stack_get_tenant_is_stack_user_project_id(self): + stack = create_stack(self.ctx, self.template, self.user_creds, + stack_user_project_id='astackuserproject') + self.ctx.tenant_id = 'astackuserproject' + ret_stack = db_api.stack_get(self.ctx, stack.id, show_deleted=False) + self.assertIsNotNone(ret_stack) + self.assertEqual(stack.id, ret_stack.id) + self.assertEqual('db_test_stack_name', ret_stack.name) + def test_stack_get_can_return_a_stack_from_different_tenant(self): stack = create_stack(self.ctx, self.template, self.user_creds) self.ctx.tenant_id = 'abc'