Skip to content

Commit 08b61bf

Browse files
extend listusers API to filter by source
1 parent 91ab67c commit 08b61bf

File tree

4 files changed

+37
-4
lines changed

4 files changed

+37
-4
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
// under the License.
1717
package org.apache.cloudstack.api.command.admin.user;
1818

19+
import com.cloud.exception.InvalidParameterValueException;
1920
import com.cloud.server.ResourceIcon;
2021
import com.cloud.server.ResourceTag;
2122
import com.cloud.user.Account;
23+
import com.cloud.user.User;
2224
import org.apache.cloudstack.acl.RoleType;
2325
import org.apache.cloudstack.api.command.user.UserCmd;
2426
import org.apache.cloudstack.api.response.ResourceIconResponse;
@@ -30,6 +32,7 @@
3032
import org.apache.cloudstack.api.ResponseObject.ResponseView;
3133
import org.apache.cloudstack.api.response.ListResponse;
3234
import org.apache.cloudstack.api.response.UserResponse;
35+
import org.apache.commons.lang3.EnumUtils;
3336

3437
import java.util.List;
3538

@@ -63,6 +66,10 @@ public class ListUsersCmd extends BaseListAccountResourcesCmd implements UserCmd
6366
description = "flag to display the resource icon for users")
6467
private Boolean showIcon;
6568

69+
@Parameter(name = ApiConstants.USER_SOURCE, type = CommandType.STRING, since = "4.21.0.0",
70+
description = "List users by their authentication source. Valid values are: native, LDAP, SAML2 and SAML2DISABLED.")
71+
private String userSource;
72+
6673
/////////////////////////////////////////////////////
6774
/////////////////// Accessors ///////////////////////
6875
/////////////////////////////////////////////////////
@@ -91,6 +98,23 @@ public Boolean getShowIcon() {
9198
return showIcon != null ? showIcon : false;
9299
}
93100

101+
public User.Source getUserSource() {
102+
if (userSource == null) {
103+
return null;
104+
}
105+
106+
User.Source source = EnumUtils.getEnumIgnoreCase(User.Source.class, userSource);
107+
if (source == null || source == User.Source.OAUTH2) {
108+
throw new InvalidParameterValueException(String.format("Invalid user source: %s. Valid values are: native, LDAP, SAML2 and SAML2DISABLED.", source));
109+
}
110+
111+
if (source == User.Source.NATIVE) {
112+
return User.Source.UNKNOWN;
113+
}
114+
115+
return source;
116+
}
117+
94118
/////////////////////////////////////////////////////
95119
/////////////// API Implementation///////////////////
96120
/////////////////////////////////////////////////////

api/src/main/java/org/apache/cloudstack/api/response/UserResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public class UserResponse extends BaseResponse implements SetResourceIconRespons
6767
@Param(description = "the account type of the user")
6868
private Integer accountType;
6969

70-
@SerializedName("usersource")
70+
@SerializedName(ApiConstants.USER_SOURCE)
7171
@Param(description = "the source type of the user in lowercase, such as native, ldap, saml2")
7272
private String userSource;
7373

server/src/main/java/com/cloud/api/query/QueryManagerImpl.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,7 @@ public ListResponse<UserResponse> searchForUsers(Long domainId, boolean recursiv
695695
String keyword = null;
696696

697697
Pair<List<UserAccountJoinVO>, Integer> result = getUserListInternal(caller, permittedAccounts, listAll, id,
698-
username, type, accountName, state, keyword, null, domainId, recursive, null);
698+
username, type, accountName, state, keyword, null, domainId, recursive, null, null);
699699
ListResponse<UserResponse> response = new ListResponse<UserResponse>();
700700
List<UserResponse> userResponses = ViewResponseHelper.createUserResponse(ResponseView.Restricted, CallContext.current().getCallingAccount().getDomainId(),
701701
result.first().toArray(new UserAccountJoinVO[result.first().size()]));
@@ -723,6 +723,7 @@ private Pair<List<UserAccountJoinVO>, Integer> searchForUsersInternal(ListUsersC
723723
Object state = cmd.getState();
724724
String keyword = cmd.getKeyword();
725725
String apiKeyAccess = cmd.getApiKeyAccess();
726+
User.Source userSource = cmd.getUserSource();
726727

727728
Long domainId = cmd.getDomainId();
728729
boolean recursive = cmd.isRecursive();
@@ -731,11 +732,11 @@ private Pair<List<UserAccountJoinVO>, Integer> searchForUsersInternal(ListUsersC
731732

732733
Filter searchFilter = new Filter(UserAccountJoinVO.class, "id", true, startIndex, pageSizeVal);
733734

734-
return getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, apiKeyAccess, domainId, recursive, searchFilter);
735+
return getUserListInternal(caller, permittedAccounts, listAll, id, username, type, accountName, state, keyword, apiKeyAccess, domainId, recursive, searchFilter, userSource);
735736
}
736737

737738
private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account caller, List<Long> permittedAccounts, boolean listAll, Long id, Object username, Object type,
738-
String accountName, Object state, String keyword, String apiKeyAccess, Long domainId, boolean recursive, Filter searchFilter) {
739+
String accountName, Object state, String keyword, String apiKeyAccess, Long domainId, boolean recursive, Filter searchFilter, User.Source userSource) {
739740
Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(domainId, recursive, null);
740741
accountMgr.buildACLSearchParameters(caller, id, accountName, null, permittedAccounts, domainIdRecursiveListProject, listAll, false);
741742
domainId = domainIdRecursiveListProject.first();
@@ -761,6 +762,7 @@ private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account calle
761762
sb.and("domainId", sb.entity().getDomainId(), Op.EQ);
762763
sb.and("accountName", sb.entity().getAccountName(), Op.EQ);
763764
sb.and("state", sb.entity().getState(), Op.EQ);
765+
sb.and("userSource", sb.entity().getSource(), Op.EQ);
764766
if (apiKeyAccess != null) {
765767
sb.and("apiKeyAccess", sb.entity().getApiKeyAccess(), Op.EQ);
766768
}
@@ -827,6 +829,10 @@ private Pair<List<UserAccountJoinVO>, Integer> getUserListInternal(Account calle
827829
}
828830
}
829831

832+
if (userSource != null) {
833+
sc.setParameters("userSource", userSource.toString());
834+
}
835+
830836
return _userAccountJoinDao.searchAndCount(sc, searchFilter);
831837
}
832838

server/src/test/java/com/cloud/api/query/QueryManagerImplTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,11 +505,13 @@ public void testSearchForUsers() {
505505
Account.Type accountType = Account.Type.ADMIN;
506506
Long domainId = 1L;
507507
String apiKeyAccess = "Disabled";
508+
User.Source userSource = User.Source.NATIVE;
508509
Mockito.when(cmd.getUsername()).thenReturn(username);
509510
Mockito.when(cmd.getAccountName()).thenReturn(accountName);
510511
Mockito.when(cmd.getAccountType()).thenReturn(accountType);
511512
Mockito.when(cmd.getDomainId()).thenReturn(domainId);
512513
Mockito.when(cmd.getApiKeyAccess()).thenReturn(apiKeyAccess);
514+
Mockito.when(cmd.getUserSource()).thenReturn(userSource);
513515

514516
UserAccountJoinVO user = new UserAccountJoinVO();
515517
DomainVO domain = Mockito.mock(DomainVO.class);
@@ -531,6 +533,7 @@ public void testSearchForUsers() {
531533
Mockito.verify(sc).setParameters("type", accountType);
532534
Mockito.verify(sc).setParameters("domainId", domainId);
533535
Mockito.verify(sc).setParameters("apiKeyAccess", false);
536+
Mockito.verify(sc).setParameters("userSource", userSource.toString());
534537
Mockito.verify(userAccountJoinDao, Mockito.times(1)).searchAndCount(
535538
any(SearchCriteria.class), any(Filter.class));
536539
}

0 commit comments

Comments
 (0)