blob: 11d99f58858e0fc1d5f38b3f1ffdd00e8051c1f0 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.user.Account;
import com.cloud.user.AccountVO;
import com.cloud.user.DomainService;
import com.cloud.user.User;
import org.apache.cloudstack.api.ResponseObject;
import org.apache.cloudstack.api.response.LdapUserResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.UserResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.ldap.LdapManager;
import org.apache.cloudstack.ldap.LdapUser;
import org.apache.cloudstack.ldap.NoLdapUserMatchingQueryException;
import org.apache.cloudstack.query.QueryService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class LdapListUsersCmdTest implements LdapConfigurationChanger {
public static final String LOCAL_DOMAIN_ID = "12345678-90ab-cdef-fedc-ba0987654321";
public static final String LOCAL_DOMAIN_NAME = "engineering";
@Mock
LdapManager ldapManager;
@Mock
QueryService queryService;
@Mock
DomainService domainService;
LdapListUsersCmd ldapListUsersCmd;
LdapListUsersCmd cmdSpy;
Domain localDomain;
MockedStatic<CallContext> callContextMocked;
@Before
public void setUp() throws NoSuchFieldException, IllegalAccessException {
ldapListUsersCmd = new LdapListUsersCmd(ldapManager, queryService);
cmdSpy = spy(ldapListUsersCmd);
callContextMocked = Mockito.mockStatic(CallContext.class);
CallContext callContextMock = Mockito.mock(CallContext.class);
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Account accountMock = Mockito.mock(Account.class);
when(accountMock.getDomainId()).thenReturn(1l);
when(callContextMock.getCallingAccount()).thenReturn(accountMock);
ldapListUsersCmd._domainService = domainService;
// no need to setHiddenField(ldapListUsersCmd, .... );
}
@After
public void tearDown() throws Exception {
callContextMocked.close();
}
/**
* given: "We have an LdapManager, QueryService and LdapListUsersCmd"
* when: "Get entity owner id is called"
* then: "a 1 should be returned"
*
*/
@Test
public void getEntityOwnerIdisOne() {
long ownerId = ldapListUsersCmd.getEntityOwnerId();
assertEquals(ownerId, 1);
}
/**
* given: "We have an LdapManager with no users, QueryService and a LdapListUsersCmd"
* when: "LdapListUsersCmd is executed"
* then: "An array of size 0 is returned"
*
* @throws NoLdapUserMatchingQueryException
*/
@Test
public void successfulEmptyResponseFromExecute() throws NoLdapUserMatchingQueryException {
Mockito.doThrow(new NoLdapUserMatchingQueryException("")).when(ldapManager).getUsers(null);
ldapListUsersCmd.execute();
assertEquals(0, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
}
/**
* given: "We have an LdapManager, one user, QueryService and a LdapListUsersCmd"
* when: "LdapListUsersCmd is executed"
* then: "a list of size not 0 is returned"
*/
@Test
public void successfulResponseFromExecute() throws NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
useSubdomain();
ldapListUsersCmd.execute();
verify(queryService, times(1)).searchForUsers(nullable(Long.class), nullable(Boolean.class));
assertNotEquals(0, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
}
/**
* given: "We have an LdapManager, QueryService and a LdapListUsersCmd"
* when: "Get command name is called"
* then: "ldapuserresponse is returned"
*/
@Test
public void successfulReturnOfCommandName() {
String commandName = ldapListUsersCmd.getCommandName();
assertEquals("ldapuserresponse", commandName);
}
/**
* given: "We have an LdapUser and a CloudStack user whose username match"
* when: "isACloudstackUser is executed"
* then: "The result is true"
*
* TODO: is this really the valid behaviour? shouldn't the user also be linked to ldap and not accidentally match?
*/
@Test
public void isACloudstackUser() {
mockACSUserSearch();
LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null);
boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser);
assertTrue(result);
}
/**
* given: "We have an LdapUser and not a matching CloudstackUser"
* when: "isACloudstackUser is executed"
* then: "The result is false"
*/
@Test
public void isNotACloudstackUser() {
doReturn(new ListResponse<UserResponse>()).when(queryService).searchForUsers(nullable(Long.class), nullable(Boolean.class));
LdapUser ldapUser = new LdapUser("rmurphy", "rmurphy@cloudstack.org", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null, false, null);
boolean result = ldapListUsersCmd.isACloudstackUser(ldapUser);
assertFalse(result);
}
/**
* test whether a value other than 'any' for 'listtype' leads to a good 'userfilter' value
*/
@Test
public void getListtypeOther() {
when(cmdSpy.getListTypeString()).thenReturn("otHer", "anY");
String userfilter = cmdSpy.getUserFilterString();
assertEquals("AnyDomain", userfilter);
userfilter = cmdSpy.getUserFilterString();
assertEquals("AnyDomain", userfilter);
}
/**
* test whether a value of 'any' for 'listtype' leads to a good 'userfilter' value
*/
@Test
public void getListtypeAny() {
when(cmdSpy.getListTypeString()).thenReturn("all");
String userfilter = cmdSpy.getUserFilterString();
assertEquals("NoFilter", userfilter);
}
/**
* test whether values for 'userfilter' yield the right filter
*/
@Test
public void getUserFilter() throws NoSuchFieldException, IllegalAccessException {
when(cmdSpy.getListTypeString()).thenReturn("otHer");
LdapListUsersCmd.UserFilter userfilter = cmdSpy.getUserFilter();
assertEquals(LdapListUsersCmd.UserFilter.ANY_DOMAIN, userfilter);
when(cmdSpy.getListTypeString()).thenReturn("anY");
userfilter = cmdSpy.getUserFilter();
assertEquals(LdapListUsersCmd.UserFilter.ANY_DOMAIN, userfilter);
}
/**
* test if the right exception is thrown on invalid input.
*/
@Test(expected = IllegalArgumentException.class)
public void getInvalidUserFilterValues() throws NoSuchFieldException, IllegalAccessException {
setHiddenField(ldapListUsersCmd, "userFilter", "false");
// unused output: LdapListUsersCmd.UserFilter userfilter =
ldapListUsersCmd.getUserFilter();
}
@Test
public void getUserFilterValues() {
assertEquals("PotentialImport", LdapListUsersCmd.UserFilter.POTENTIAL_IMPORT.toString());
assertEquals(LdapListUsersCmd.UserFilter.POTENTIAL_IMPORT, LdapListUsersCmd.UserFilter.fromString("PotentialImport"));
}
@Test(expected = IllegalArgumentException.class)
public void getInvalidUserFilterStringValue() {
LdapListUsersCmd.UserFilter.fromString("PotentImport");
}
/**
* apply no filter
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyNoFilter() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
useSubdomain();
setHiddenField(ldapListUsersCmd, "userFilter", "NoFilter");
ldapListUsersCmd.execute();
assertEquals(3, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
}
/**
* filter all acs users
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyAnyDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
useSubdomain();
setHiddenField(ldapListUsersCmd, "userFilter", "AnyDomain");
setHiddenField(ldapListUsersCmd, "domainId", 2l /* not root */);
ldapListUsersCmd.execute();
// 'rmurphy' annotated with native
// 'bob' still in
// 'abhi' is filtered out
List<ResponseObject> responses = ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses();
assertEquals(2, responses.size());
for(ResponseObject response : responses) {
if(!(response instanceof LdapUserResponse)) {
fail("unexpected return-type from API backend method");
} else {
LdapUserResponse userResponse = (LdapUserResponse)response;
// further validate this user
if ("rmurphy".equals(userResponse.getUsername()) &&
! User.Source.NATIVE.toString().equalsIgnoreCase(userResponse.getUserSource())) {
fail("expected murphy from ldap");
}
if ("bob".equals(userResponse.getUsername()) &&
! "".equals(userResponse.getUserSource())) {
fail("expected bob from without usersource");
}
}
}
}
/**
* filter out acs users for the requested domain
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyLocalDomainForASubDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
setHiddenField(ldapListUsersCmd, "userFilter", "LocalDomain");
setHiddenField(ldapListUsersCmd, "domainId", 2l /* not root */);
localDomain = useSubdomain();
ldapListUsersCmd.execute();
// 'rmurphy' filtered out 'bob' still in
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
// todo: assert user sources
}
/**
* filter out acs users for the default domain
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyLocalDomainForTheCallersDomain() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
setHiddenField(ldapListUsersCmd, "userFilter", "LocalDomain");
AccountVO account = new AccountVO();
setHiddenField(account, "accountName", "admin");
setHiddenField(account, "domainId", 1l);
final CallContext callContext = CallContext.current();
setHiddenField(callContext, "account", account);
DomainVO domainVO = useDomain("ROOT", 1l);
localDomain = domainVO;
ldapListUsersCmd.execute();
// 'rmurphy' filtered out 'bob' still in
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
// todo: assert usersources
}
/**
* todo generate an extensive configuration and check with an extensive user list
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyPotentialImport() throws NoSuchFieldException, IllegalAccessException, NoLdapUserMatchingQueryException {
mockACSUserSearch();
mockResponseCreation();
useSubdomain();
setHiddenField(ldapListUsersCmd, "userFilter", "PotentialImport");
ldapListUsersCmd.execute();
assertEquals(2, ((ListResponse)ldapListUsersCmd.getResponseObject()).getResponses().size());
}
/**
* unknown filter
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test(expected = IllegalArgumentException.class)
public void applyUnknownFilter() throws NoSuchFieldException, IllegalAccessException {
setHiddenField(ldapListUsersCmd, "userFilter", "UnknownFilter");
ldapListUsersCmd.execute();
}
/**
* make sure there are no unimplemented filters
*
* This was created to deal with the possible {code}NoSuchMethodException{code} that won't be dealt with in regular coverage
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
@Test
public void applyUnimplementedFilter() throws NoSuchFieldException, IllegalAccessException {
useSubdomain();
for (LdapListUsersCmd.UserFilter UNIMPLEMENTED_FILTER : LdapListUsersCmd.UserFilter.values()) {
setHiddenField(ldapListUsersCmd, "userFilter", UNIMPLEMENTED_FILTER.toString());
ldapListUsersCmd.getUserFilter().filter(ldapListUsersCmd,new ArrayList<LdapUserResponse>());
}
}
// helper methods //
////////////////////
private DomainVO useSubdomain() {
DomainVO domainVO = useDomain(LOCAL_DOMAIN_NAME, 2l);
return domainVO;
}
private DomainVO useDomain(String domainName, long domainId) {
DomainVO domainVO = new DomainVO();
domainVO.setName(domainName);
domainVO.setId(domainId);
domainVO.setUuid(LOCAL_DOMAIN_ID);
when(domainService.getDomain(nullable(Long.class))).thenReturn(domainVO);
return domainVO;
}
private void mockACSUserSearch() {
UserResponse rmurphy = createMockUserResponse("rmurphy", User.Source.NATIVE);
UserResponse rohit = createMockUserResponse("rohit", User.Source.SAML2);
UserResponse abhi = createMockUserResponse("abhi", User.Source.LDAP);
ArrayList<UserResponse> responses = new ArrayList<>();
responses.add(rmurphy);
responses.add(rohit);
responses.add(abhi);
ListResponse<UserResponse> queryServiceResponse = new ListResponse<>();
queryServiceResponse.setResponses(responses);
doReturn(queryServiceResponse).when(queryService).searchForUsers(nullable(Long.class), nullable(Boolean.class));
}
private UserResponse createMockUserResponse(String uid, User.Source source) {
UserResponse userResponse = new UserResponse();
userResponse.setUsername(uid);
userResponse.setUserSource(source);
// for now:
userResponse.setDomainId(LOCAL_DOMAIN_ID);
userResponse.setDomainName(LOCAL_DOMAIN_NAME);
return userResponse;
}
private void mockResponseCreation() throws NoLdapUserMatchingQueryException {
List<LdapUser> users = new ArrayList();
LdapUser murphy = new LdapUser("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", "mythical", false, null);
LdapUser bob = new LdapUser("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME, false, null);
LdapUser abhi = new LdapUser("abhi", "abhi@test.com", "Abhi", "YoungOrOld", "cn=abhi,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME, false, null);
users.add(murphy);
users.add(bob);
users.add(abhi);
doReturn(users).when(ldapManager).getUsers(any());
LdapUserResponse response = new LdapUserResponse("rmurphy", "rmurphy@test.com", "Ryan", "Murphy", "cn=rmurphy,dc=cloudstack,dc=org", null);
doReturn(response).when(ldapManager).createLdapUserResponse(murphy);
LdapUserResponse bobResponse = new LdapUserResponse("bob", "bob@test.com", "Robert", "Young", "cn=bob,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME);
doReturn(bobResponse).when(ldapManager).createLdapUserResponse(bob);
LdapUserResponse abhiResponse = new LdapUserResponse("abhi", "abhi@test.com", "Abhi", "YoungOrOld", "cn=abhi,ou=engineering,dc=cloudstack,dc=org", LOCAL_DOMAIN_NAME);
doReturn(abhiResponse).when(ldapManager).createLdapUserResponse(abhi);
}
}