Only delete stack user project from correct domain

Check the domain ID for the project matches the configured domain from
heat.conf, so we avoid the bad outcome of deleting the user's project
when heat has been configured for stack domain users after creating
some stacks.

Change-Id: I247e732033f44daf0de5b0efeff6c263814d13ab
Closes-Bug: #1365332
This commit is contained in:
Steven Hardy 2014-09-04 22:10:47 +01:00
parent f31647d17b
commit e9b5a4a549
2 changed files with 109 additions and 10 deletions

View File

@ -422,10 +422,8 @@ class KeystoneClientV3(object):
return user.id
def _check_stack_domain_user(self, user_id, project_id, action):
"""Sanity check that domain/project is correct."""
user = self.domain_admin_client.users.get(user_id)
@property
def stack_domain_id(self):
if not self._stack_domain_id:
if self._stack_domain_is_id:
self._stack_domain_id = self.stack_domain
@ -433,8 +431,13 @@ class KeystoneClientV3(object):
domain = self.domain_admin_client.domains.get(
self.stack_domain)
self._stack_domain_id = domain.id
return self._stack_domain_id
if user.domain_id != self._stack_domain_id:
def _check_stack_domain_user(self, user_id, project_id, action):
"""Sanity check that domain/project is correct."""
user = self.domain_admin_client.users.get(user_id)
if user.domain_id != self.stack_domain_id:
raise ValueError(_('User %s in invalid domain') % action)
if user.default_project_id != project_id:
raise ValueError(_('User %s in invalid project') % action)
@ -484,8 +487,25 @@ class KeystoneClientV3(object):
LOG.warning(_('Falling back to legacy non-domain project, '
'configure domain in heat.conf'))
return
# If stacks are created before configuring the heat domain, they
# exist in the default domain, in the user's project, which we
# do *not* want to delete! However, if the keystone v3cloudsample
# policy is used, it's possible that we'll get Forbidden when trying
# to get the project, so again we should do nothing
try:
self.domain_admin_client.projects.delete(project=project_id)
project = self.domain_admin_client.projects.get(project=project_id)
except kc_exception.Forbidden:
LOG.warning(_('Unable to get details for project %s, not deleting')
% project_id)
return
if project.domain_id != self.stack_domain_id:
LOG.warning(_('Not deleting non heat-domain project'))
return
try:
project.delete()
except kc_exception.NotFound:
pass

View File

@ -1241,16 +1241,80 @@ class KeystoneClientTest(HeatTestCase):
self._stub_domain_admin_client()
self.mock_admin_client.projects = self.m.CreateMockAnything()
self.mock_admin_client.projects.delete(project='aprojectid')
self.mock_admin_client.projects.delete(project='aprojectid').AndRaise(
kc_exception.NotFound)
dummy = self.m.CreateMockAnything()
dummy.id = 'aproject123'
dummy.domain_id = 'adomain123'
dummy.delete().AndReturn(None)
self.mock_admin_client.projects.get(project='aprojectid').AndReturn(
dummy)
self.m.ReplayAll()
ctx = utils.dummy_context()
ctx.trust_id = None
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
# Second delete will raise ignored NotFound
def test_delete_stack_domain_project_notfound(self):
"""Test the delete_stack_domain_project function."""
self._stub_domain_admin_client()
self.mock_admin_client.projects = self.m.CreateMockAnything()
dummy = self.m.CreateMockAnything()
dummy.id = 'aproject123'
dummy.domain_id = 'adomain123'
dummy.delete().AndRaise(kc_exception.NotFound)
self.mock_admin_client.projects.get(project='aprojectid').AndReturn(
dummy)
self.m.ReplayAll()
ctx = utils.dummy_context()
ctx.trust_id = None
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
def test_delete_stack_domain_project_forbidden(self):
"""Test the delete_stack_domain_project function."""
self._stub_domain_admin_client()
self.mock_admin_client.projects = self.m.CreateMockAnything()
self.mock_admin_client.projects.get(project='aprojectid').AndRaise(
kc_exception.Forbidden)
self.m.ReplayAll()
ctx = utils.dummy_context()
ctx.trust_id = None
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
def test_delete_stack_domain_project_wrongdomain(self):
"""Test the delete_stack_domain_project function."""
self._stub_domain_admin_client()
self.mock_admin_client.projects = self.m.CreateMockAnything()
dummy = self.m.CreateMockAnything()
dummy.id = 'aproject123'
dummy.domain_id = 'default'
self.mock_admin_client.projects.get(project='aprojectid').AndReturn(
dummy)
self.m.ReplayAll()
ctx = utils.dummy_context()
ctx.trust_id = None
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
def test_delete_stack_domain_project_nodomain(self):
"""Test the delete_stack_domain_project function."""
self._clear_domain_override()
ctx = utils.dummy_context()
ctx.trust_id = None
heat_ks_client = heat_keystoneclient.KeystoneClient(ctx)
heat_ks_client.delete_stack_domain_project(project_id='aprojectid')
def _stub_domain_user_pw_auth(self):
@ -1460,3 +1524,18 @@ class KeystoneClientTestDomainName(KeystoneClientTest):
self._stub_domain_admin_client_domain_get()
p = super(KeystoneClientTestDomainName, self)
p.test_disable_stack_domain_user_error_domain()
def test_delete_stack_domain_project(self):
self._stub_domain_admin_client_domain_get()
p = super(KeystoneClientTestDomainName, self)
p.test_delete_stack_domain_project()
def test_delete_stack_domain_project_notfound(self):
self._stub_domain_admin_client_domain_get()
p = super(KeystoneClientTestDomainName, self)
p.test_delete_stack_domain_project_notfound()
def test_delete_stack_domain_project_wrongdomain(self):
self._stub_domain_admin_client_domain_get()
p = super(KeystoneClientTestDomainName, self)
p.test_delete_stack_domain_project_wrongdomain()