mirror of
https://github.com/apache/cloudstack
synced 2026-09-04 16:34:34 +00:00
Add missing project cleanups while deleting/moving user (#13817)
Co-authored-by: Bernardo De Marco Gonçalves <bernardomg2004@gmail.com>
This commit is contained in:
parent
121d93164c
commit
64178ead02
@ -23,6 +23,7 @@ import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.projects.ProjectAccount.Role;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
|
||||
public interface ProjectService {
|
||||
/**
|
||||
@ -102,4 +103,5 @@ public interface ProjectService {
|
||||
|
||||
boolean addUserToProject(Long projectId, String username, String email, Long projectRoleId, Role projectRole) throws ResourceAllocationException;
|
||||
|
||||
void moveProjectAssociationsToUser(User oldUser, User newUser) throws ResourceAllocationException;
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.api.command.admin.user;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
@ -112,7 +113,7 @@ public class MoveUserCmd extends BaseCmd {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
public void execute() throws ResourceAllocationException {
|
||||
Preconditions.checkNotNull(getId(),"I have to have an user to move!");
|
||||
Preconditions.checkState(ObjectUtils.anyNotNull(getAccountId(),getAccountName()),"provide either an account name or an account id!");
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.region;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.api.command.admin.account.DeleteAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.DisableAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.EnableAccountCmd;
|
||||
@ -116,7 +117,7 @@ public interface RegionService {
|
||||
* @param moveUserCmd
|
||||
* @return true if delete was successful, false otherwise
|
||||
*/
|
||||
boolean moveUser(MoveUserCmd moveUserCmd);
|
||||
boolean moveUser(MoveUserCmd moveUserCmd) throws ResourceAllocationException;
|
||||
|
||||
/**
|
||||
* update an existing domain
|
||||
|
||||
@ -110,6 +110,10 @@ public class ProjectAccountVO implements ProjectAccount, InternalIdentity {
|
||||
return projectAccountId;
|
||||
}
|
||||
|
||||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
public void setProjectRoleId(Long projectRoleId) {
|
||||
this.projectRoleId = projectRoleId;
|
||||
}
|
||||
|
||||
@ -102,6 +102,10 @@ public class ProjectInvitationVO implements ProjectInvitation {
|
||||
return forAccountId;
|
||||
}
|
||||
|
||||
public void setForAccountId(Long forAccountId) {
|
||||
this.forAccountId = forAccountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
return token;
|
||||
|
||||
@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import com.cloud.projects.ProjectAccount;
|
||||
import com.cloud.projects.ProjectAccountVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface ProjectAccountDao extends GenericDao<ProjectAccountVO, Long> {
|
||||
@ -47,9 +48,11 @@ public interface ProjectAccountDao extends GenericDao<ProjectAccountVO, Long> {
|
||||
|
||||
void removeAccountFromProjects(long accountId);
|
||||
|
||||
void removeUserFromProjects(long userId);
|
||||
|
||||
boolean canUserModifyProject(long projectId, long accountId, long userId);
|
||||
|
||||
List<ProjectAccountVO> listUsersOrAccountsByRole(long id);
|
||||
|
||||
List<ProjectAccountVO> listBy(Long projectId, Long accountId, Long userId);
|
||||
|
||||
void move(User oldUser, User newUser);
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package com.cloud.projects.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.user.User;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.projects.ProjectAccount;
|
||||
@ -192,17 +193,6 @@ public class ProjectAccountDaoImpl extends GenericDaoBase<ProjectAccountVO, Long
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUserFromProjects(long userId) {
|
||||
SearchCriteria<ProjectAccountVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("userId", userId);
|
||||
|
||||
int removedCount = remove(sc);
|
||||
if (removedCount > 0) {
|
||||
logger.debug(String.format("Removed user [%s] from %s project(s).", userId, removedCount));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUserModifyProject(long projectId, long accountId, long userId) {
|
||||
SearchCriteria<ProjectAccountVO> sc = AllFieldsSearch.create();
|
||||
@ -222,4 +212,23 @@ public class ProjectAccountDaoImpl extends GenericDaoBase<ProjectAccountVO, Long
|
||||
sc.setParameters("projectRoleId", id);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectAccountVO> listBy(Long projectId, Long accountId, Long userId) {
|
||||
SearchCriteria<ProjectAccountVO> sc = AllFieldsSearch.create();
|
||||
sc.setParametersIfNotNull("projectId", projectId);
|
||||
sc.setParametersIfNotNull("userId", userId);
|
||||
sc.setParametersIfNotNull("accountId", accountId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(User oldUser, User newUser) {
|
||||
List<ProjectAccountVO> projectAccounts = listBy(null, oldUser.getAccountId(), oldUser.getId());
|
||||
for (ProjectAccountVO projectAccount : projectAccounts) {
|
||||
projectAccount.setAccountId(newUser.getAccountId());
|
||||
projectAccount.setUserId(newUser.getId());
|
||||
update(projectAccount.getId(), projectAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import com.cloud.projects.ProjectInvitation.State;
|
||||
import com.cloud.projects.ProjectInvitationVO;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface ProjectInvitationDao extends GenericDao<ProjectInvitationVO, Long> {
|
||||
@ -43,4 +44,9 @@ public interface ProjectInvitationDao extends GenericDao<ProjectInvitationVO, Lo
|
||||
|
||||
List<ProjectInvitationVO> listInvitationsToExpire(long timeOut);
|
||||
|
||||
int removeBy(Long projectId, Long accountId, Long userId);
|
||||
|
||||
List<ProjectInvitationVO> listBy(Long projectId, Long accountId, Long userId);
|
||||
|
||||
void move(User oldUser, User newUser);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package com.cloud.projects.dao;
|
||||
import java.sql.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.user.User;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.projects.ProjectInvitation.State;
|
||||
@ -124,6 +125,40 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeBy(Long projectId, Long accountId, Long userId) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = prepareAllFieldsSearchCriteria(projectId, accountId, userId);
|
||||
return remove(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectInvitationVO> listBy(Long projectId, Long accountId, Long userId) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = prepareAllFieldsSearchCriteria(projectId, accountId, userId);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(User oldUser, User newUser) {
|
||||
List<ProjectInvitationVO> projectInvitations = listBy(null, oldUser.getAccountId(), oldUser.getId());
|
||||
for (ProjectInvitationVO projectInvitation : projectInvitations) {
|
||||
projectInvitation.setForAccountId(newUser.getAccountId());
|
||||
projectInvitation.setForUserId(newUser.getId());
|
||||
update(projectInvitation.getId(), projectInvitation);
|
||||
}
|
||||
}
|
||||
|
||||
private SearchCriteria<ProjectInvitationVO> prepareAllFieldsSearchCriteria(Long projectId, Long accountId, Long userId) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
|
||||
|
||||
sc.setParametersIfNotNull("userId", userId);
|
||||
sc.setParametersIfNotNull("accountId", accountId);
|
||||
if (projectId != null && projectId != -1) {
|
||||
sc.setParameters("projectId", projectId);
|
||||
}
|
||||
|
||||
return sc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(long id, long timeout) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
|
||||
|
||||
@ -18,3 +18,7 @@
|
||||
--;
|
||||
-- Schema upgrade cleanup from 4.22.1.0 to 4.23.0.0
|
||||
--;
|
||||
|
||||
-- Delete stale project association entries for users that were removed
|
||||
DELETE FROM `cloud`.`project_account` WHERE `user_id` IN (SELECT `id` FROM `cloud`.`user` WHERE `removed` IS NOT NULL);
|
||||
DELETE FROM `cloud`.`project_invitations` WHERE `user_id` IN (SELECT `id` FROM `cloud`.`user` WHERE `removed` IS NOT NULL);
|
||||
|
||||
@ -23,6 +23,7 @@ import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.auth.UserAuthenticator;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
@ -170,7 +171,12 @@ public class LdapAuthenticator extends AdapterBase implements UserAuthenticator
|
||||
if (mappedAccount == null || mappedAccount.getRemoved() != null) {
|
||||
throw new CloudRuntimeException("Mapped account for users does not exist. Please contact your administrator.");
|
||||
}
|
||||
_accountManager.moveUser(userAccount.getId(), userAccount.getDomainId(), mappedAccount);
|
||||
try {
|
||||
_accountManager.moveUser(userAccount.getId(), userAccount.getDomainId(), mappedAccount);
|
||||
} catch (ResourceAllocationException e) {
|
||||
throw new CloudRuntimeException(String.format("Failed to move User [%s] to mapped Account [%s] due to insufficient Project limits.",
|
||||
userAccount.getUsername(), mappedAccount.getAccountName()), e);
|
||||
}
|
||||
}
|
||||
// else { the user hasn't changed in ldap, the ldap group stayed the same, hurray, pass, fun thou self a lot of fun }
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ package com.cloud.projects;
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
|
||||
public interface ProjectManager extends ProjectService {
|
||||
@ -47,6 +48,8 @@ public interface ProjectManager extends ProjectService {
|
||||
|
||||
long getInvitationTimeout();
|
||||
|
||||
boolean cleanupProjectsForUser(Project project, User user);
|
||||
|
||||
public static final String MESSAGE_CREATE_TUNGSTEN_PROJECT_EVENT = "Message.CreateTungstenProject.Event";
|
||||
public static final String MESSAGE_DELETE_TUNGSTEN_PROJECT_EVENT = "Message.DeleteTungstenProject.Event";
|
||||
|
||||
|
||||
@ -617,6 +617,37 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers all project associations and project invitations from one user to another.
|
||||
*
|
||||
* @param oldUser the user whose project associations are being transferred
|
||||
* @param newUser the user to whom the project associations are being transferred
|
||||
* @throws ResourceAllocationException if there is an issue with allocating the required project resources to the new user
|
||||
*/
|
||||
@Override
|
||||
public void moveProjectAssociationsToUser(User oldUser, User newUser) throws ResourceAllocationException {
|
||||
_projectInvitationDao.move(oldUser, newUser);
|
||||
|
||||
List<ProjectAccountVO> projectAccounts = _projectAccountDao.listBy(null, oldUser.getAccountId(), oldUser.getId());
|
||||
if (projectAccounts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Account oldAccount = _accountDao.findById(oldUser.getAccountId());
|
||||
Account newAccount = _accountDao.findById(newUser.getAccountId());
|
||||
long requiredProjectsAmount = oldAccount.getId() != newAccount.getId()
|
||||
? projectAccounts.stream().filter(pa -> pa.getAccountRole() == ProjectAccount.Role.Admin).count()
|
||||
: 0L;
|
||||
|
||||
try (CheckedReservation projectReservation = new CheckedReservation(newAccount, ResourceType.project, null, null, requiredProjectsAmount, reservationDao, _resourceLimitMgr)) {
|
||||
_projectAccountDao.move(oldUser, newUser);
|
||||
if (requiredProjectsAmount > 0) {
|
||||
_resourceLimitMgr.incrementResourceCount(newAccount.getId(), ResourceType.project, requiredProjectsAmount);
|
||||
_resourceLimitMgr.decrementResourceCount(oldAccount.getId(), ResourceType.project, requiredProjectsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project findByNameAndDomainId(String name, long domainId) {
|
||||
return _projectDao.findByNameAndDomain(name, domainId);
|
||||
@ -1033,50 +1064,41 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
|
||||
//verify permissions
|
||||
_accountMgr.checkAccess(caller, AccessType.ModifyProject, true, _accountMgr.getAccount(project.getProjectAccountId()));
|
||||
|
||||
//Check if the user exists in the project
|
||||
ProjectAccount projectUser = _projectAccountDao.findByProjectIdUserId(projectId, user.getAccountId(), user.getId());
|
||||
if (projectUser == null) {
|
||||
deletePendingInvite(projectId, user);
|
||||
boolean success = cleanupProjectsForUser(project, user);
|
||||
if (!success) {
|
||||
InvalidParameterValueException ex = new InvalidParameterValueException("User " + user.getUsername() + " is not assigned to the project with specified id");
|
||||
// Use the projectVO object and not the projectAccount object to inject the projectId.
|
||||
ex.addProxyObject(project.getUuid(), "projectId");
|
||||
throw ex;
|
||||
}
|
||||
return deleteUserFromProject(projectId, user);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deletePendingInvite(Long projectId, User user) {
|
||||
ProjectInvitation invite = _projectInvitationDao.findByUserIdProjectId(user.getId(), user.getAccountId(), projectId);
|
||||
if (invite != null) {
|
||||
boolean success = _projectInvitationDao.remove(invite.getId());
|
||||
if (success){
|
||||
logger.info("Successfully deleted invite pending for the user : {}", user);
|
||||
} else {
|
||||
logger.info("Failed to delete project invite for user: {}", user);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cleans up project associations and invitations for a specified user in a given project.
|
||||
*
|
||||
* @param project the project from which the user is being cleaned up; if null, cleanup applies to all projects associated with the user
|
||||
* @param user the user whose project associations and invitations are being cleaned up
|
||||
* @return true if any project accounts associated with the user were removed, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean cleanupProjectsForUser(Project project, User user) {
|
||||
return Transaction.execute((TransactionCallback<Boolean>) status -> {
|
||||
Long projectId = project != null ? project.getId() : null;
|
||||
long userId = user.getId();
|
||||
long accountId = user.getAccountId();
|
||||
|
||||
@DB
|
||||
private boolean deleteUserFromProject(Long projectId, User user) {
|
||||
return Transaction.execute(new TransactionCallback<Boolean>() {
|
||||
@Override
|
||||
public Boolean doInTransaction(TransactionStatus status) {
|
||||
boolean success = true;
|
||||
ProjectAccountVO projectAccount = _projectAccountDao.findByProjectIdUserId(projectId, user.getAccountId(), user.getId());
|
||||
success = _projectAccountDao.remove(projectAccount.getId());
|
||||
_projectInvitationDao.removeBy(projectId, accountId, userId);
|
||||
|
||||
List<ProjectAccountVO> projectAccounts = _projectAccountDao.listBy(projectId, accountId, userId);
|
||||
for (ProjectAccountVO projectAccount : projectAccounts) {
|
||||
_projectAccountDao.remove(projectAccount.getId());
|
||||
if (projectAccount.getAccountRole() == Role.Admin) {
|
||||
_resourceLimitMgr.decrementResourceCount(user.getAccountId(), ResourceType.project);
|
||||
_resourceLimitMgr.decrementResourceCount(accountId, ResourceType.project);
|
||||
}
|
||||
if (success) {
|
||||
logger.debug("Removed user {} from project. Removing any invite sent to the user", user);
|
||||
ProjectInvitation invite = _projectInvitationDao.findByUserIdProjectId(user.getId(), user.getAccountId(), projectId);
|
||||
if (invite != null) {
|
||||
success = success && _projectInvitationDao.remove(invite.getId());
|
||||
}
|
||||
}
|
||||
return success;
|
||||
logger.debug("Removed user [{}] from project [{}].", user, projectAccount.getProjectId());
|
||||
}
|
||||
|
||||
return !projectAccounts.isEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.apikeypair.ApiKeyPair;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
@ -148,7 +149,7 @@ public interface AccountManager extends AccountService, Configurable {
|
||||
* moves a user to another account within the same domain
|
||||
* @return true if the user was successfully moved
|
||||
*/
|
||||
boolean moveUser(MoveUserCmd moveUserCmd);
|
||||
boolean moveUser(MoveUserCmd moveUserCmd) throws ResourceAllocationException;
|
||||
|
||||
@Override
|
||||
UserAccount updateUser(UpdateUserCmd cmd);
|
||||
@ -190,7 +191,7 @@ public interface AccountManager extends AccountService, Configurable {
|
||||
ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<Boolean>("Advanced", Boolean.class, "use.secret.key.in.response", "false",
|
||||
"This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true);
|
||||
|
||||
boolean moveUser(long id, Long domainId, Account newAccount);
|
||||
boolean moveUser(long id, Long domainId, Account newAccount) throws ResourceAllocationException;
|
||||
|
||||
UserTwoFactorAuthenticator getUserTwoFactorAuthenticator(final Long domainId, final Long userAccountId);
|
||||
|
||||
|
||||
@ -45,10 +45,13 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.projects.dao.ProjectInvitationDao;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.SSHKeyPairDao;
|
||||
import com.cloud.user.dao.UserAccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.utils.db.TransactionCallbackWithException;
|
||||
import org.apache.cloudstack.acl.APIChecker;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairManagerImpl;
|
||||
import org.apache.cloudstack.acl.ApiKeyPairPermissionVO;
|
||||
@ -315,6 +318,8 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
@Inject
|
||||
private ProjectAccountDao _projectAccountDao;
|
||||
@Inject
|
||||
private ProjectInvitationDao projectInvitationDao;
|
||||
@Inject
|
||||
private IPAddressDao _ipAddressDao;
|
||||
@Inject
|
||||
private HostDao hostDao;
|
||||
@ -2521,14 +2526,29 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
checkAccountAndAccess(user, account);
|
||||
verifyCallerPrivilegeForUserOrAccountOperations(user);
|
||||
|
||||
removeUserApiKeys(id);
|
||||
return deleteAndCleanupUser(user);
|
||||
}
|
||||
|
||||
return _userDao.remove(id);
|
||||
/**
|
||||
* Removes the specified user and performs cleanup operations associated with the user.
|
||||
*
|
||||
* @param user the user to be deleted and cleaned up
|
||||
* @return true if the user was successfully marked as removed, false otherwise
|
||||
*/
|
||||
protected boolean deleteAndCleanupUser(User user) {
|
||||
return Transaction.execute((TransactionCallback<Boolean>) status -> {
|
||||
long userId = user.getId();
|
||||
|
||||
removeUserApiKeys(userId);
|
||||
_projectMgr.cleanupProjectsForUser(null, user);
|
||||
|
||||
return _userDao.remove(userId);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ActionEvent(eventType = EventTypes.EVENT_USER_MOVE, eventDescription = "moving User to a new account")
|
||||
public boolean moveUser(MoveUserCmd cmd) {
|
||||
public boolean moveUser(MoveUserCmd cmd) throws ResourceAllocationException {
|
||||
final Long id = cmd.getId();
|
||||
UserVO user = getValidUserVO(id);
|
||||
Account oldAccount = _accountDao.findById(user.getAccountId());
|
||||
@ -2542,7 +2562,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean moveUser(long id, Long domainId, Account newAccount) {
|
||||
public boolean moveUser(long id, Long domainId, Account newAccount) throws ResourceAllocationException {
|
||||
UserVO user = getValidUserVO(id);
|
||||
Account oldAccount = _accountDao.findById(user.getAccountId());
|
||||
checkAccountAndAccess(user, oldAccount);
|
||||
@ -2550,24 +2570,22 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
return moveUser(user, newAccount.getId());
|
||||
}
|
||||
|
||||
private boolean moveUser(UserVO user, long newAccountId) {
|
||||
private boolean moveUser(UserVO user, long newAccountId) throws ResourceAllocationException {
|
||||
if (newAccountId == user.getAccountId()) {
|
||||
// could do a not silent fail but the objective of the user is reached
|
||||
return true; // no need to create a new user object for this user
|
||||
}
|
||||
|
||||
return Transaction.execute(new TransactionCallback<>() {
|
||||
@Override
|
||||
public Boolean doInTransaction(TransactionStatus status) {
|
||||
UserVO newUser = new UserVO(user);
|
||||
user.setExternalEntity(user.getUuid());
|
||||
user.setUuid(UUID.randomUUID().toString());
|
||||
_userDao.update(user.getId(), user);
|
||||
newUser.setAccountId(newAccountId);
|
||||
boolean success = _userDao.remove(user.getId());
|
||||
UserVO persisted = _userDao.persist(newUser);
|
||||
return success && persisted.getUuid().equals(user.getExternalEntity());
|
||||
}
|
||||
return Transaction.execute((TransactionCallbackWithException<Boolean, ResourceAllocationException>) status -> {
|
||||
UserVO newUser = new UserVO(user);
|
||||
user.setExternalEntity(user.getUuid());
|
||||
user.setUuid(UUID.randomUUID().toString());
|
||||
_userDao.update(user.getId(), user);
|
||||
newUser.setAccountId(newAccountId);
|
||||
UserVO persisted = _userDao.persist(newUser);
|
||||
_projectMgr.moveProjectAssociationsToUser(user, persisted);
|
||||
boolean success = _userDao.remove(user.getId());
|
||||
return success && persisted.getUuid().equals(user.getExternalEntity());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ package org.apache.cloudstack.region;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.domain.UpdateDomainCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
@ -128,7 +129,7 @@ public interface RegionManager {
|
||||
* @param moveUserCmd
|
||||
* @return
|
||||
*/
|
||||
boolean moveUser(MoveUserCmd moveUserCmd);
|
||||
boolean moveUser(MoveUserCmd moveUserCmd) throws ResourceAllocationException;
|
||||
|
||||
/**
|
||||
* update an existing domain
|
||||
|
||||
@ -24,6 +24,7 @@ import java.util.Properties;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -227,7 +228,7 @@ public class RegionManagerImpl extends ManagerBase implements RegionManager, Man
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean moveUser(MoveUserCmd cmd) {
|
||||
public boolean moveUser(MoveUserCmd cmd) throws ResourceAllocationException {
|
||||
return _accountMgr.moveUser(cmd);
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import org.apache.cloudstack.api.command.admin.account.DeleteAccountCmd;
|
||||
@ -154,7 +155,7 @@ public class RegionServiceImpl extends ManagerBase implements RegionService, Man
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean moveUser(MoveUserCmd cmd) {
|
||||
public boolean moveUser(MoveUserCmd cmd) throws ResourceAllocationException {
|
||||
return _regionMgr.moveUser(cmd);
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@ import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.projects.ProjectAccount.Role;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
@ -214,6 +215,17 @@ public class MockProjectManagerImpl extends ManagerBase implements ProjectManage
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanupProjectsForUser(Project project, User user) {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveProjectAssociationsToUser(User oldUser, User newUser) throws ResourceAllocationException {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public Project findByProjectAccountIdIncludingRemoved(long projectAccountId) {
|
||||
return null;
|
||||
|
||||
@ -17,9 +17,11 @@
|
||||
package com.cloud.projects;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.reservation.dao.ReservationDao;
|
||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.junit.Assert;
|
||||
@ -28,6 +30,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.Spy;
|
||||
@ -35,7 +38,17 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import com.cloud.configuration.Resource.ResourceType;
|
||||
import com.cloud.exception.ResourceAllocationException;
|
||||
import com.cloud.projects.ProjectAccount.Role;
|
||||
import com.cloud.projects.dao.ProjectAccountDao;
|
||||
import com.cloud.projects.dao.ProjectDao;
|
||||
import com.cloud.projects.dao.ProjectInvitationDao;
|
||||
import com.cloud.resourcelimit.CheckedReservation;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
|
||||
|
||||
@ -49,6 +62,21 @@ public class ProjectManagerImplTest {
|
||||
@Mock
|
||||
ProjectDao projectDao;
|
||||
|
||||
@Mock
|
||||
ProjectInvitationDao projectInvitationDao;
|
||||
|
||||
@Mock
|
||||
ProjectAccountDao projectAccountDao;
|
||||
|
||||
@Mock
|
||||
AccountDao accountDao;
|
||||
|
||||
@Mock
|
||||
ResourceLimitService resourceLimitMgr;
|
||||
|
||||
@Mock
|
||||
ReservationDao reservationDao;
|
||||
|
||||
List<ProjectVO> updateProjects;
|
||||
|
||||
@Before
|
||||
@ -128,4 +156,158 @@ public class ProjectManagerImplTest {
|
||||
Assert.assertTrue(CollectionUtils.isEmpty(result));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupProjectsForUserTestNoAssociationsReturnsFalse() {
|
||||
Project project = Mockito.mock(Project.class);
|
||||
Mockito.when(project.getId()).thenReturn(100L);
|
||||
User user = mockUser(1L, 10L);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong()))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
boolean result = projectManager.cleanupProjectsForUser(project, user);
|
||||
|
||||
Assert.assertFalse(result);
|
||||
Mockito.verify(projectInvitationDao).removeBy(100L, 10L, 1L);
|
||||
Mockito.verify(projectAccountDao, Mockito.never()).remove(Mockito.anyLong());
|
||||
Mockito.verify(resourceLimitMgr, Mockito.never()).decrementResourceCount(Mockito.anyLong(), Mockito.any(ResourceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupProjectsForUserTestRemovesAdminAndRegularAssociations() {
|
||||
Project project = Mockito.mock(Project.class);
|
||||
Mockito.when(project.getId()).thenReturn(100L);
|
||||
User user = mockUser(1L, 10L);
|
||||
ProjectAccountVO admin = mockProjectAccount(1L, Role.Admin);
|
||||
ProjectAccountVO regular = mockProjectAccount(2L, Role.Regular);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong()))
|
||||
.thenReturn(List.of(admin, regular));
|
||||
|
||||
boolean result = projectManager.cleanupProjectsForUser(project, user);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
Mockito.verify(projectInvitationDao).removeBy(100L, 10L, 1L);
|
||||
Mockito.verify(projectAccountDao).remove(1L);
|
||||
Mockito.verify(projectAccountDao).remove(2L);
|
||||
Mockito.verify(resourceLimitMgr).decrementResourceCount(10L, ResourceType.project);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cleanupProjectsForUserTestNullProject() {
|
||||
User user = mockUser(1L, 10L);
|
||||
ProjectAccountVO admin = mockProjectAccount(1L, Role.Admin);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L)))
|
||||
.thenReturn(List.of(admin));
|
||||
|
||||
boolean result = projectManager.cleanupProjectsForUser(null, user);
|
||||
|
||||
Assert.assertTrue(result);
|
||||
Mockito.verify(projectInvitationDao).removeBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L));
|
||||
Mockito.verify(projectAccountDao).remove(1L);
|
||||
Mockito.verify(resourceLimitMgr).decrementResourceCount(10L, ResourceType.project);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveProjectAssociationsToUserTestNoProjectAccounts() throws ResourceAllocationException {
|
||||
User oldUser = mockUser(1L, 10L);
|
||||
User newUser = mockUser(2L, 20L);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L)))
|
||||
.thenReturn(Collections.emptyList());
|
||||
|
||||
projectManager.moveProjectAssociationsToUser(oldUser, newUser);
|
||||
|
||||
Mockito.verify(projectInvitationDao).move(oldUser, newUser);
|
||||
Mockito.verify(projectAccountDao, Mockito.never()).move(Mockito.any(), Mockito.any());
|
||||
Mockito.verifyNoInteractions(accountDao);
|
||||
Mockito.verifyNoInteractions(resourceLimitMgr);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveProjectAssociationsToUserTestSameAccount() throws ResourceAllocationException {
|
||||
User oldUser = mockUser(1L, 10L);
|
||||
User newUser = mockUser(2L, 10L);
|
||||
ProjectAccountVO regular = mockProjectAccount(1L, Role.Regular);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L)))
|
||||
.thenReturn(List.of(regular));
|
||||
AccountVO oldAccount = mockAccount(10L);
|
||||
AccountVO newAccount = mockAccount(10L);
|
||||
Mockito.when(accountDao.findById(10L)).thenReturn(oldAccount).thenReturn(newAccount);
|
||||
|
||||
try (MockedConstruction<CheckedReservation> ignored = Mockito.mockConstruction(CheckedReservation.class)) {
|
||||
projectManager.moveProjectAssociationsToUser(oldUser, newUser);
|
||||
}
|
||||
|
||||
Mockito.verify(projectInvitationDao).move(oldUser, newUser);
|
||||
Mockito.verify(projectAccountDao).move(oldUser, newUser);
|
||||
Mockito.verify(resourceLimitMgr, Mockito.never())
|
||||
.incrementResourceCount(Mockito.anyLong(), Mockito.any(ResourceType.class), Mockito.anyLong());
|
||||
Mockito.verify(resourceLimitMgr, Mockito.never())
|
||||
.decrementResourceCount(Mockito.anyLong(), Mockito.any(ResourceType.class), Mockito.anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveProjectAssociationsToUserTestDifferentAccountsWithAdminRole() throws ResourceAllocationException {
|
||||
User oldUser = mockUser(1L, 10L);
|
||||
User newUser = mockUser(2L, 20L);
|
||||
ProjectAccountVO admin = mockProjectAccount(1L, Role.Admin);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L)))
|
||||
.thenReturn(List.of(admin));
|
||||
AccountVO oldAccount = mockAccount(10L);
|
||||
AccountVO newAccount = mockAccount(20L);
|
||||
Mockito.when(accountDao.findById(10L)).thenReturn(oldAccount);
|
||||
Mockito.when(accountDao.findById(20L)).thenReturn(newAccount);
|
||||
|
||||
try (MockedConstruction<CheckedReservation> ignored = Mockito.mockConstruction(CheckedReservation.class)) {
|
||||
projectManager.moveProjectAssociationsToUser(oldUser, newUser);
|
||||
}
|
||||
|
||||
Mockito.verify(projectInvitationDao).move(oldUser, newUser);
|
||||
Mockito.verify(projectAccountDao).move(oldUser, newUser);
|
||||
Mockito.verify(resourceLimitMgr).incrementResourceCount(20L, ResourceType.project, 1L);
|
||||
Mockito.verify(resourceLimitMgr).decrementResourceCount(10L, ResourceType.project, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveProjectAssociationsToUserTestDifferentAccountsWithoutAdminRole() throws ResourceAllocationException {
|
||||
User oldUser = mockUser(1L, 10L);
|
||||
User newUser = mockUser(2L, 20L);
|
||||
ProjectAccountVO regular = mockProjectAccount(1L, Role.Regular);
|
||||
Mockito.when(projectAccountDao.listBy(Mockito.isNull(), Mockito.eq(10L), Mockito.eq(1L)))
|
||||
.thenReturn(List.of(regular));
|
||||
AccountVO oldAccount = mockAccount(10L);
|
||||
AccountVO newAccount = mockAccount(20L);
|
||||
Mockito.when(accountDao.findById(10L)).thenReturn(oldAccount);
|
||||
Mockito.when(accountDao.findById(20L)).thenReturn(newAccount);
|
||||
|
||||
try (MockedConstruction<CheckedReservation> ignored = Mockito.mockConstruction(CheckedReservation.class)) {
|
||||
projectManager.moveProjectAssociationsToUser(oldUser, newUser);
|
||||
}
|
||||
|
||||
Mockito.verify(projectInvitationDao).move(oldUser, newUser);
|
||||
Mockito.verify(projectAccountDao).move(oldUser, newUser);
|
||||
Mockito.verify(resourceLimitMgr, Mockito.never())
|
||||
.incrementResourceCount(Mockito.anyLong(), Mockito.any(ResourceType.class), Mockito.anyLong());
|
||||
Mockito.verify(resourceLimitMgr, Mockito.never())
|
||||
.decrementResourceCount(Mockito.anyLong(), Mockito.any(ResourceType.class), Mockito.anyLong());
|
||||
}
|
||||
|
||||
private User mockUser(long id, long accountId) {
|
||||
User user = Mockito.mock(User.class);
|
||||
Mockito.when(user.getId()).thenReturn(id);
|
||||
Mockito.when(user.getAccountId()).thenReturn(accountId);
|
||||
return user;
|
||||
}
|
||||
|
||||
private AccountVO mockAccount(long id) {
|
||||
AccountVO account = Mockito.mock(AccountVO.class);
|
||||
Mockito.when(account.getId()).thenReturn(id);
|
||||
return account;
|
||||
}
|
||||
|
||||
private ProjectAccountVO mockProjectAccount(long id, Role role) {
|
||||
ProjectAccountVO projectAccount = Mockito.mock(ProjectAccountVO.class);
|
||||
Mockito.when(projectAccount.getId()).thenReturn(id);
|
||||
Mockito.when(projectAccount.getAccountRole()).thenReturn(role);
|
||||
return projectAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2131,4 +2131,16 @@ public class AccountManagerImplTest extends AccountManagentImplTestBase {
|
||||
);
|
||||
Assert.assertNotNull(userResultVO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteAndCleanupUserTestUserCleanup() {
|
||||
long userId = userVoMock.getId();
|
||||
Mockito.doNothing().when(accountManagerImpl).removeUserApiKeys(userId);
|
||||
Mockito.doReturn(true).when(_projectMgr).cleanupProjectsForUser(null, userVoMock);
|
||||
|
||||
accountManagerImpl.deleteAndCleanupUser(userVoMock);
|
||||
|
||||
Mockito.verify(accountManagerImpl).removeUserApiKeys(userId);
|
||||
Mockito.verify(_projectMgr).cleanupProjectsForUser(null, userVoMock);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user