Skip to content

Commit

Permalink
ldap trust map cleanup on domain delete
Browse files Browse the repository at this point in the history
  • Loading branch information
DaanHoogland committed Aug 28, 2023
1 parent 5559668 commit 77d8d7b
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import java.util.Map;
import java.util.UUID;

import com.cloud.domain.Domain;
import com.cloud.user.AccountManager;
import com.cloud.user.DomainManager;
import com.cloud.utils.component.ComponentLifecycleBase;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.api.LdapValidator;
Expand Down Expand Up @@ -107,6 +109,13 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
super.configure(name, params);
LOGGER.debug("Configuring LDAP Manager");

addAccountRemovalListener();
addDomainRemovalListener();

return true;
}

private void addAccountRemovalListener() {
messageBus.subscribe(AccountManager.MESSAGE_REMOVE_ACCOUNT_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
Expand All @@ -125,10 +134,28 @@ public void onPublishMessage(String senderAddress, String subject, Object args)
}
}
});

return true;
}

private void addDomainRemovalListener() {
messageBus.subscribe(DomainManager.MESSAGE_REMOVE_DOMAIN_EVENT, new MessageSubscriber() {
@Override
public void onPublishMessage(String senderAddress, String subject, Object args) {
try {
final Domain domain = domainDao.findByIdIncludingRemoved((Long) args);
long domainId = domain.getId();
LdapTrustMapVO ldapTrustMapVO = _ldapTrustMapDao.findByDomainId(domainId);
if (ldapTrustMapVO != null) {
String msg = String.format("Removing link between LDAP: %s - type: %s on domain: %s",
ldapTrustMapVO.getName(), ldapTrustMapVO.getType().name(), domainId);
LOGGER.debug(msg);
_ldapTrustMapDao.remove(ldapTrustMapVO.getId());
}
} catch (final Exception e) {
LOGGER.error("Caught exception while removing trust-map for domain linked to LDAP", e);
}
}
});
}
@Override
public LdapConfigurationResponse addConfiguration(final LdapAddConfigurationCmd cmd) throws InvalidParameterValueException {
return addConfigurationInternal(cmd.getHostname(),cmd.getPort(),cmd.getDomainId());
Expand Down
42 changes: 25 additions & 17 deletions server/src/main/java/com/cloud/user/AccountManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -1812,31 +1812,16 @@ public boolean deleteUserAccount(long accountId) {
// If the user is a System user, return an error. We do not allow this
AccountVO account = _accountDao.findById(accountId);

if (account == null || account.getRemoved() != null) {
if (account != null) {
s_logger.info("The account:" + account.getAccountName() + " is already removed");
}
if (checkDeleteNeeded(account, caller)) {
return true;
}

// don't allow removing Project account
if (account == null || account.getType() == Account.Type.PROJECT) {
throw new InvalidParameterValueException("The specified account does not exist in the system");
}

checkAccess(caller, null, true, account);

// don't allow to delete default account (system and admin)
if (account.isDefault()) {
throw new InvalidParameterValueException("The account is default and can't be removed");
}

// Account that manages project(s) can't be removed
List<Long> managedProjectIds = _projectAccountDao.listAdministratedProjectIds(accountId);
if (!managedProjectIds.isEmpty()) {
StringBuilder projectIds = new StringBuilder();
for (Long projectId : managedProjectIds) {
projectIds.append(projectId + ", ");
projectIds.append(projectId).append(", ");
}

throw new InvalidParameterValueException("The account id=" + accountId + " manages project(s) with ids " + projectIds + "and can't be removed");
Expand All @@ -1847,6 +1832,29 @@ public boolean deleteUserAccount(long accountId) {
return deleteAccount(account, callerUserId, caller);
}

private boolean checkDeleteNeeded(AccountVO account, Account caller) {
if (account == null) {
s_logger.info("The account:" + account.getAccountName() + " doesn't exist");
return true;
}
if (account.getRemoved() != null) {
s_logger.info("The account:" + account.getAccountName() + " is already removed");
return true;
}
// don't allow removing Project account
if (account.getType() == Account.Type.PROJECT) {
throw new InvalidParameterValueException("The specified account does not exist in the system");
}

checkAccess(caller, null, true, account);

// don't allow to delete default account (system and admin)
if (account.isDefault()) {
throw new InvalidParameterValueException("The account is default and can't be removed");
}
return false;
}

@Override
@ActionEvent(eventType = EventTypes.EVENT_ACCOUNT_ENABLE, eventDescription = "enabling account", async = true)
public AccountVO enableAccount(String accountName, Long domainId, Long accountId) {
Expand Down
88 changes: 50 additions & 38 deletions server/src/main/java/com/cloud/user/DomainManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;

import com.cloud.api.query.dao.DiskOfferingJoinDao;
Expand Down Expand Up @@ -344,56 +345,67 @@ public boolean deleteDomain(long domainId, Boolean cleanup) {

@Override
public boolean deleteDomain(DomainVO domain, Boolean cleanup) {
GlobalLock lock = getGlobalLock("AccountCleanup");
GlobalLock lock = getGlobalLock();
if (lock == null) return false;

try {
// mark domain as inactive
s_logger.debug("Marking domain id=" + domain.getId() + " as " + Domain.State.Inactive + " before actually deleting it");
domain.setState(Domain.State.Inactive);
_domainDao.update(domain.getId(), domain);

return cleanDomain(domain, cleanup);
}
finally {
lock.unlock();
}
}

@Nullable
private GlobalLock getGlobalLock() {
GlobalLock lock = getGlobalLock("DomainCleanup");
if (lock == null) {
s_logger.debug("Couldn't get the global lock");
return false;
return null;
}

if (!lock.lock(30)) {
s_logger.debug("Couldn't lock the db");
return false;
return null;
}
return lock;
}

private boolean cleanDomain(DomainVO domain, Boolean cleanup) {
try {
// mark domain as inactive
s_logger.debug("Marking domain id=" + domain.getId() + " as " + Domain.State.Inactive + " before actually deleting it");
domain.setState(Domain.State.Inactive);
_domainDao.update(domain.getId(), domain);

try {
long ownerId = domain.getAccountId();
if (BooleanUtils.toBoolean(cleanup)) {
tryCleanupDomain(domain, ownerId);
} else {
removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(domain);
}
long ownerId = domain.getAccountId();
if (BooleanUtils.toBoolean(cleanup)) {
tryCleanupDomain(domain, ownerId);
} else {
removeDomainWithNoAccountsForCleanupNetworksOrDedicatedResources(domain);
}

if (!_configMgr.releaseDomainSpecificVirtualRanges(domain.getId())) {
CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because failed to release domain specific virtual ip ranges");
e.addProxyObject(domain.getUuid(), "domainId");
throw e;
} else {
s_logger.debug("Domain specific Virtual IP ranges " + " are successfully released as a part of domain id=" + domain.getId() + " cleanup.");
}
if (!_configMgr.releaseDomainSpecificVirtualRanges(domain.getId())) {
CloudRuntimeException e = new CloudRuntimeException("Can't delete the domain yet because failed to release domain specific virtual ip ranges");
e.addProxyObject(domain.getUuid(), "domainId");
throw e;
} else {
s_logger.debug("Domain specific Virtual IP ranges " + " are successfully released as a part of domain id=" + domain.getId() + " cleanup.");
}

cleanupDomainDetails(domain.getId());
cleanupDomainOfferings(domain.getId());
annotationDao.removeByEntityType(AnnotationService.EntityType.DOMAIN.name(), domain.getUuid());
CallContext.current().putContextParameter(Domain.class, domain.getUuid());
return true;
} catch (Exception ex) {
s_logger.error("Exception deleting domain with id " + domain.getId(), ex);
if (ex instanceof CloudRuntimeException) {
rollbackDomainState(domain);
throw (CloudRuntimeException)ex;
}
else
return false;
cleanupDomainDetails(domain.getId());
cleanupDomainOfferings(domain.getId());
annotationDao.removeByEntityType(AnnotationService.EntityType.DOMAIN.name(), domain.getUuid());
CallContext.current().putContextParameter(Domain.class, domain.getUuid());
return true;
} catch (Exception ex) {
s_logger.error("Exception deleting domain with id " + domain.getId(), ex);
if (ex instanceof CloudRuntimeException) {
rollbackDomainState(domain);
throw (CloudRuntimeException)ex;
}
}
finally {
lock.unlock();
else
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public class DomainManagerImplTest {
public void setup() throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Mockito.doReturn(adminAccount).when(domainManager).getCaller();
Mockito.doReturn(lock).when(domainManager).getGlobalLock("AccountCleanup");
Mockito.doReturn(lock).when(domainManager).getGlobalLock("DomainCleanup");
Mockito.when(lock.lock(Mockito.anyInt())).thenReturn(true);
Mockito.when(domainDaoMock.findById(DOMAIN_ID)).thenReturn(domain);
Mockito.when(domain.getAccountId()).thenReturn(ACCOUNT_ID);
Expand Down

0 comments on commit 77d8d7b

Please sign in to comment.