8000 User search parameter for service account inclusion/exclusion by resah · Pull Request #48033 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,42 @@ List<UserRepresentation> searchByAttributes(@QueryParam("first") Integer firstRe
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> searchByLastName(@QueryParam("lastName") String email, @QueryParam("exact") Boolean exact);

/**
* Search for users matching the given search string, optionally including or excluding service accounts.
*
* @param search arbitrary search string, or {@code null} to list all users
* @param enabled Boolean representing if user is enabled or not
* @param includeServiceAccounts Boolean, which defines whether result list should include service accounts; {@code null} keeps the default behavior
* @return a list of {@link UserRepresentation}
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> searchByUserAccounts(@QueryParam("search") String search,
@QueryParam("enabled") Boolean enabled,
@QueryParam("includeServiceAccounts") Boolean includeServiceAccounts);

/**
* Search for users matching the given search string, optionally including or excluding service accounts, with pagination.
*
* @param search arbitrary search string, or {@code null} to list all users
* @param enabled Boolean representing if user is enabled or not
* @param includeServiceAccounts Boolean, which defines whether result list should include service accounts; {@code null} keeps the default behavior
* @param firstResult the position of the first result to retrieve
* @param maxResults the maximum number of results to retrieve
* @param briefRepresentation Only return basic information (only guaranteed to return id, username, created, first and last name,
* email, enabled state, email verification state, federation link, and access.
* Note that it means that namely user attributes, required actions, and not before are not returned.)
* @return a list of {@link UserRepresentation}
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
List<UserRepresentation> searchByUserAccounts(@QueryParam("search") String search,
@QueryParam("enabled") Boolean enabled,
@QueryParam("includeServiceAccounts") Boolean includeServiceAccounts,
@QueryParam("first") Integer firstResult,
@QueryParam("max") Integer maxResults,
@QueryParam("briefRepresentation") Boolean briefRepresentation);

/**
* Search for users based on the given filters.
*
Expand Down Expand Up @@ -344,6 +380,21 @@ List<UserRepresentation> list(@QueryParam("first") Integer firstResult,
@Produces(MediaType.APPLICATION_JSON)
Integer count(@QueryParam("search") String search);

/**
* Returns the users that can be viewed and match the given filters.
*
* @param search criteria to search for
* @param enabled Boolean representing if user is enabled or not
* @param includeServiceAccounts Boolean, which defines whether result list should include service accounts
* @return number of users matching the search criteria
*/
@Path("count")
@GET
@Produces(MediaType.APPLICATION_JSON)
Integer count(@QueryParam("search") String search,
@QueryParam("enabled") Boolean enabled,
@QueryParam("includeServiceAccounts") Boolean includeServiceAccounts);

/**
* Returns the number of users that can be viewed and match the given filters.
* If none of the filters is specified this is equivalent to {{@link #count()}}.
Expand Down Expand Up @@ -504,6 +555,44 @@ Integer count(@QueryParam("search") String search,
@QueryParam("createdAfter") String createdAfter,
@QueryParam("createdBefore") String createdBefore);

/**
* Returns the number of users that can be viewed and match the given filters,
* including creation timestamp filters and the service account inclusion flag.
*
* @param search arbitrary search string for all the fields below
* @param last last name field of a user
* @param first first name field of a user
* @param email email field of a user
* @param emailVerified emailVerified field of a user
* @param username username field of a user
* @param enabled Boolean representing if user is enabled or not
* @param idpAlias The alias of an Identity Provider linked to the user
* @param idpUserId The userId at an Identity Provider linked to the user
* @param exact Boolean which defines whether the params must match exactly
* @param searchQuery A query to search for custom attributes
* @param createdAfter only count users created after (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds
* @param createdBefore only count users created before (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds
* @param includeServiceAccounts Boolean, which defines whether service accounts are counted; {@code null} keeps the default behavior
* @return number of users matching the given filters
*/
@Path("count")
@GET
@Produces(MediaType.APPLICATION_JSON)
Integer count(@QueryParam("search") String search,
@QueryParam("lastName") String last,
@QueryParam("firstName") String first,
@QueryParam("email") String email,
@QueryParam("emailVerified") Boolean emailVerified,
@QueryParam("username") String username,
@QueryParam("enabled") Boolean enabled,
@QueryParam("idpAlias") String idpAlias,
@QueryParam("idpUserId") String idpUserId,
@QueryParam("exact") Boolean exact,
@QueryParam("q") String searchQuery,
@QueryParam("createdAfter") String createdAfter,
@QueryParam("createdBefore") String createdBefore,
@QueryParam("includeServiceAccounts") Boolean includeServiceAccounts);

/**
* Returns the number of users with the given status for emailVerified.
* If none of the filters is specified this is equivalent to {{@link #count()}}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ public UserResource user(final @PathParam("user-id") String id) {
* @param briefRepresentation Boolean which defines whether brief representations are returned (default: false)
* @param exact Boolean which defines whether the params "last", "first", "email" and "username" must match exactly
* @param searchQuery A query to search for custom attributes, in the format 'key1:value2 key2:value2'
* @param includeServiceAccounts Boolean, which defines whether result list should include service accounts. When "true" then always included, "false" then never, null will fall back to known legacy behavior (prefixed searches such as id:, username: or email: include service accounts)
* @return a non-null {@code Stream} of users
*/
@GET
Expand Down Expand Up @@ -299,7 +300,8 @@ public Stream<UserRepresentation> getUsers(
@Parameter(description = "Boolean which defines whether the params \"last\", \"first\", \"email\" and \"username\" must match exactly") @QueryParam("exact") Boolean exact,
@Parameter(description = "A query to search for custom attributes, in the format 'key1:value2 key2:value2'") @QueryParam("q") String searchQuery,
@Parameter(description = "Only return users created after (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdAfter") String createdAfter,
@Parameter(description = "Only return users created before (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdBefore") String createdBefore) {
@Parameter(description = "Only return users created before (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdBefore") String createdBefore,
@Parameter(description = "Boolean, which defines whether result list should include service accounts. When \"true\" then always included, \"false\" then never, null will fall back to known legacy behavior (prefixed searches such as id:, username: or email: include service accounts)") @QueryParam("includeServiceAccounts") Optional<Boolean> includeServiceAccounts) {
UserPermissionEvaluator userPermissionEvaluator = auth.users();

userPermissionEvaluator.requireQuery();
Expand All @@ -317,7 +319,8 @@ public Stream<UserRepresentation> getUsers(
if (prefix != null) {
userModels = Arrays.stream(prefix.splitTerms(search))
.map(term -> prefix.lookup(session.users(), realm, term))
.filter(Objects::nonNull);
.filter(Objects::nonNull)
.filter(user -> includeServiceAccounts.orElse(true) || !user.isServiceAccount());
if (AdminPermissionsSchema.SCHEMA.isAdminPermissionsEnabled(realm)) {
userModels = userModels.filter(userPermissionEvaluator::canView);
}
Expand All @@ -333,7 +336,7 @@ public Stream<UserRepresentation> getUsers(
addCreatedTimestampConditions(attributes, createdAfter, createdBefore);

return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult,
maxResults, false);
maxResults, includeServiceAccounts.orElse(false));
}
} else if (last != null || first != null || email != null || username != null || emailVerified != null
|| idpAlias != null || idpUserId != null || enabled != null || exact != null || !searchAttributes.isEmpty()
Expand Down Expand Up @@ -371,10 +374,10 @@ public Stream<UserRepresentation> getUsers(
attributes.putAll(searchAttributes);

return searchForUser(attributes, realm, userPermissionEvaluator, briefRepresentation, firstResult,
maxResults, true);
maxResults, includeServiceAccounts.orElse(true));
} else {
return searchForUser(new HashMap<>(), realm, userPermissionEvaluator, briefRepresentation,
firstResult, maxResults, false);
firstResult, maxResults, includeServiceAccounts.orElse(false));
}

return toRepresentation(realm, userPermissionEvaluator, briefRepresentation, userModels);
Expand Down Expand Up @@ -406,6 +409,7 @@ public Stream<UserRepresentation> getUsers(
* @param enabled Boolean representing if user is enabled or not
* @param exact Boolean which defines whether the params "last", "first", "email" and "username" must match exactly
* @param searchQuery A query to search for custom attributes, in the format 'key1:value2 key2:value2'
* @param includeServiceAccounts Boolean, which defines whether result list should include service accounts. When "true" then always included, "false" then never, null will fall back to known legacy behavior (prefixed searches such as id:, username: or email: include service accounts)
* @return the number of users that match the given criteria
*/
@Path("count")
Expand Down Expand Up @@ -436,7 +440,8 @@ public Integer getUsersCount(
@Parameter(description = "Boolean which defines whether the params \"last\", \"first\", \"email\" and \"username\" must match exactly") @QueryParam("exact") Boolean exact,
@Parameter(description = "A query to search for custom attributes, in the format 'key1:value2 key2:value2'") @QueryParam("q") String searchQuery,
@Parameter(description = "Only return users created after (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdAfter") String createdAfter,
@Parameter(description = "Only return users created before (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdBefore") String createdBefore) {
@Parameter(description = "Only return users created before (inclusive) the given date, in ISO-8601 format (yyyy-MM-dd) or epoch milliseconds") @QueryParam("createdBefore") String createdBefore,
@Parameter(description = "Boolean, which defines whether result list should include service accounts. When \"true\" then always included, \"false\" then never, null will fall back to known legacy behavior (prefixed searches such as id:, username: or email: include service accounts)") @QueryParam("includeServiceAccounts") Optional<Boolean> includeServiceAccounts) {
UserPermissionEvaluator userPermissionEvaluator = auth.users();
userPermissionEvaluator.requireQuery();

Expand All @@ -449,6 +454,7 @@ public Integer getUsersCount(
return (int) Arrays.stream(prefix.splitTerms(search))
.map(term -> prefix.lookup(session.users(), realm, term))
.filter(Objects::nonNull)
.filter(user -> includeServiceAccounts.orElse(true) || !user.isServiceAccount())
.filter(userPermissionEvaluator::canView)
.count();
}
Expand All @@ -464,7 +470,7 @@ public Integer getUsersCount(
}
addCreatedTimestampConditions(parameters, createdAfter, createdBefore);
// search /users equivalent to this doesn't include service-accounts so counting shouldn't as well
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, "false");
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts.orElse(false).toString());
if (userPermissionEvaluator.canView()) {
return session.users().getUsersCount(realm, parameters);
} else {
Expand Down Expand Up @@ -508,7 +514,7 @@ public Integer getUsersCount(
addCreatedTimestampConditions(parameters, createdAfter, createdBefore);
parameters.putAll(searchAttributes);
// search /users equivalent to this does include service-accounts so we should be explicit
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, "true");
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts.orElse(true).toString());
if (userPermissionEvaluator.canView()) {
return session.users().getUsersCount(realm, parameters);
} else {
Expand All @@ -521,7 +527,7 @@ public Integer getUsersCount(
} else {
Map<String, String> parameters = new HashMap<>();
// list /users equivalent to this doesn't include service-accounts so counting shouldn't as well
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, "false");
parameters.put(UserModel.INCLUDE_SERVICE_ACCOUNT, includeServiceAccounts.orElse(false).toString());
if (userPermissionEvaluator.canView()) {
return session.users().getUsersCount(realm, parameters);
} else {
Expand Down
Loading
Loading
0