From 869ffad6b3879495cbf01a6896db74a2691ef709 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 18 Nov 2025 20:16:43 +0530 Subject: [PATCH] server,engine-schema: add check for account userdata cleanup Fixes #9477 Signed-off-by: Abhishek Kumar --- .../com/cloud/storage/dao/VMTemplateDao.java | 2 ++ .../cloud/storage/dao/VMTemplateDaoImpl.java | 18 ++++++++++++++++++ .../java/com/cloud/user/dao/UserDataDao.java | 3 +++ .../com/cloud/user/AccountManagerImpl.java | 16 +++++++++++++++- 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java index c751f81f9273..e9ebf42c8378 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDao.java @@ -99,4 +99,6 @@ public interface VMTemplateDao extends GenericDao, StateDao< List listByIds(List ids); List listIdsByTemplateTag(String tag); + + List listByUserdataIdsNotAccount(List userdataIds, long accountId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java index b6796cf8f9d1..465bd25152c4 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplateDaoImpl.java @@ -863,4 +863,22 @@ public boolean updateState( } return rows > 0; } + + @Override + public List listByUserdataIdsNotAccount(List userdataIds, long accountId) { + if (CollectionUtils.isEmpty(userdataIds)) { + return Collections.emptyList(); + } + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(userDataSearch.entity().getId()); + sb.and("userDataId", sb.entity().getUserDataId(), SearchCriteria.Op.EQ); + sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); + sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.NEQ); + sb.done(); + SearchCriteria sc = sb.create(); + sc.setParameters("userDataId", userdataIds.toArray()); + sc.setParameters("state", VirtualMachineTemplate.State.Active.toString()); + sc.setParameters("accountId", accountId); + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/com/cloud/user/dao/UserDataDao.java b/engine/schema/src/main/java/com/cloud/user/dao/UserDataDao.java index 2733d8d2782f..e516ea1e6a88 100644 --- a/engine/schema/src/main/java/com/cloud/user/dao/UserDataDao.java +++ b/engine/schema/src/main/java/com/cloud/user/dao/UserDataDao.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.user.dao; +import java.util.List; + import com.cloud.user.UserDataVO; import com.cloud.utils.db.GenericDao; @@ -25,6 +27,7 @@ public interface UserDataDao extends GenericDao { public UserDataVO findByName(long accountId, long domainId, String name); + List listByAccountId(long accountId); int removeByAccountId(long accountId); } diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index 2f6392ffaad2..f83201fe3692 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -475,6 +475,20 @@ public void setQuerySelectors(List querySelectors) { _querySelectors = querySelectors; } + protected void deleteUserDataForAccount(long accountId) { + List userdataList = userDataDao.listByAccountId(accountId); + if (CollectionUtils.isNotEmpty(userdataList)) { + List conflictingTemplateIds = _templateDao.listByUserdataIdsNotAccount(userdataList + .stream() + .map(UserDataVO::getId) + .collect(Collectors.toList()), accountId); + if (CollectionUtils.isNotEmpty(conflictingTemplateIds)) { + throw new CloudRuntimeException("User data owned by account linked to templates not owned by the account"); + } + } + userDataDao.removeByAccountId(accountId); + } + protected void deleteWebhooksForAccount(long accountId) { try { WebhookHelper webhookService = ComponentContext.getDelegateComponentOfType(WebhookHelper.class); @@ -1200,7 +1214,7 @@ public int compare(NetworkVO network1, NetworkVO network2) { } // Delete registered UserData - userDataDao.removeByAccountId(accountId); + deleteUserDataForAccount(accountId); // Delete Webhooks deleteWebhooksForAccount(accountId);