Skip to content

Commit c8a4575

Browse files
lucas-a-martinsLucas Martins
and
Lucas Martins
authored
Fix deleteUser API to prevent deletion of the caller (#8691)
Co-authored-by: Lucas Martins <lucas.martins@scclouds.com.br>
1 parent d171582 commit c8a4575

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/user/DeleteUserCmd.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,10 @@ public ApiCommandResourceType getApiResourceType() {
8484
public void execute() {
8585
CallContext.current().setEventDetails("UserId: " + getId());
8686
boolean result = _regionService.deleteUser(this);
87-
if (result) {
88-
SuccessResponse response = new SuccessResponse(getCommandName());
89-
this.setResponseObject(response);
90-
} else {
87+
if (!result) {
9188
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to delete user");
9289
}
90+
SuccessResponse response = new SuccessResponse(getCommandName());
91+
this.setResponseObject(response);
9392
}
9493
}

server/src/main/java/com/cloud/user/AccountManagerImpl.java

+12-5
Original file line numberDiff line numberDiff line change
@@ -2064,13 +2064,20 @@ public AccountVO updateAccount(UpdateAccountCmd cmd) {
20642064
@Override
20652065
@ActionEvent(eventType = EventTypes.EVENT_USER_DELETE, eventDescription = "deleting User")
20662066
public boolean deleteUser(DeleteUserCmd deleteUserCmd) {
2067-
UserVO user = getValidUserVO(deleteUserCmd.getId());
2068-
2067+
final Long id = deleteUserCmd.getId();
2068+
User caller = CallContext.current().getCallingUser();
2069+
UserVO user = getValidUserVO(id);
20692070
Account account = _accountDao.findById(user.getAccountId());
20702071

2072+
if (caller.getId() == id) {
2073+
Domain domain = _domainDao.findById(account.getDomainId());
2074+
throw new InvalidParameterValueException(String.format("The caller is requesting to delete itself. As a security measure, ACS will not allow this operation." +
2075+
" To delete user %s (ID: %s, Domain: %s), request to another user with permission to execute the operation.", user.getUsername(), user.getUuid(), domain.getUuid()));
2076+
}
2077+
20712078
// don't allow to delete the user from the account of type Project
20722079
checkAccountAndAccess(user, account);
2073-
return _userDao.remove(deleteUserCmd.getId());
2080+
return _userDao.remove(id);
20742081
}
20752082

20762083
@Override
@@ -2145,7 +2152,7 @@ private void checkIfNotMovingAcrossDomains(long domainId, Account newAccount) {
21452152
}
21462153
}
21472154

2148-
private void checkAccountAndAccess(UserVO user, Account account) {
2155+
protected void checkAccountAndAccess(UserVO user, Account account) {
21492156
// don't allow to delete the user from the account of type Project
21502157
if (account.getType() == Account.Type.PROJECT) {
21512158
throw new InvalidParameterValueException("Project users cannot be deleted or moved.");
@@ -2155,7 +2162,7 @@ private void checkAccountAndAccess(UserVO user, Account account) {
21552162
CallContext.current().putContextParameter(User.class, user.getUuid());
21562163
}
21572164

2158-
private UserVO getValidUserVO(long id) {
2165+
protected UserVO getValidUserVO(long id) {
21592166
UserVO user = _userDao.findById(id);
21602167

21612168
if (user == null || user.getRemoved() != null) {

server/src/test/java/com/cloud/user/AccountManagerImplTest.java

+44
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.cloud.vm.VMInstanceVO;
3535
import com.cloud.vm.snapshot.VMSnapshotVO;
3636
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
37+
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
3738
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
3839
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
3940
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
@@ -48,6 +49,7 @@
4849
import org.junit.runner.RunWith;
4950
import org.mockito.InOrder;
5051
import org.mockito.Mock;
52+
import org.mockito.MockedStatic;
5153
import org.mockito.Mockito;
5254
import org.mockito.junit.MockitoJUnitRunner;
5355

@@ -91,6 +93,12 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
9193
@Mock
9294
private Account accountMock;
9395

96+
@Mock
97+
private DomainVO domainVoMock;
98+
99+
@Mock
100+
private AccountVO accountVoMock;
101+
94102
@Mock
95103
private ProjectAccountVO projectAccountVO;
96104
@Mock
@@ -190,6 +198,42 @@ public void deleteUserAccountCleanup() {
190198
Mockito.verify(_accountDao, Mockito.atLeastOnce()).markForCleanup(Mockito.eq(42l));
191199
}
192200

201+
@Test (expected = InvalidParameterValueException.class)
202+
public void deleteUserTestIfUserIdIsEqualToCallerIdShouldThrowException() {
203+
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
204+
DeleteUserCmd cmd = Mockito.mock(DeleteUserCmd.class);
205+
CallContext callContextMock = Mockito.mock(CallContext.class);
206+
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
207+
208+
Mockito.doReturn(userVoMock).when(callContextMock).getCallingUser();
209+
Mockito.doReturn(1L).when(cmd).getId();
210+
Mockito.doReturn(userVoMock).when(accountManagerImpl).getValidUserVO(Mockito.anyLong());
211+
Mockito.doReturn(accountVoMock).when(_accountDao).findById(Mockito.anyLong());
212+
Mockito.doReturn(domainVoMock).when(_domainDao).findById(Mockito.anyLong());
213+
Mockito.doReturn(1L).when(userVoMock).getId();
214+
215+
accountManagerImpl.deleteUser(cmd);
216+
}
217+
}
218+
219+
@Test
220+
public void deleteUserTestIfUserIdIsNotEqualToCallerIdShouldNotThrowException() {
221+
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
222+
DeleteUserCmd cmd = Mockito.mock(DeleteUserCmd.class);
223+
CallContext callContextMock = Mockito.mock(CallContext.class);
224+
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
225+
226+
Mockito.doReturn(userVoMock).when(callContextMock).getCallingUser();
227+
Mockito.doReturn(1L).when(cmd).getId();
228+
Mockito.doReturn(userVoMock).when(accountManagerImpl).getValidUserVO(Mockito.anyLong());
229+
Mockito.doReturn(accountVoMock).when(_accountDao).findById(Mockito.anyLong());
230+
Mockito.doReturn(2L).when(userVoMock).getId();
231+
232+
Mockito.doNothing().when(accountManagerImpl).checkAccountAndAccess(Mockito.any(), Mockito.any());
233+
accountManagerImpl.deleteUser(cmd);
234+
}
235+
}
236+
193237
@Test
194238
public void testAuthenticateUser() throws UnknownHostException {
195239
Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> successAuthenticationPair = new Pair<>(true, null);

0 commit comments

Comments
 (0)