ProfileUtis.java
package com.bonitasoft.processbuilder.extension;
import org.bonitasoft.engine.api.APIAccessor;
import org.bonitasoft.engine.api.IdentityAPI;
import org.bonitasoft.engine.api.ProfileAPI;
import org.bonitasoft.engine.identity.MemberType;
import org.bonitasoft.engine.identity.User;
import org.bonitasoft.engine.identity.UserCriterion;
import org.bonitasoft.engine.identity.UserSearchDescriptor;
import org.bonitasoft.engine.profile.Profile;
import org.bonitasoft.engine.profile.ProfileMember;
import org.bonitasoft.engine.profile.ProfileMemberSearchDescriptor;
import org.bonitasoft.engine.profile.ProfileSearchDescriptor;
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.search.SearchResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Utility class for retrieving user information based on Bonita profiles.
* Provides static methods to collect all user IDs associated with a profile through
* direct membership, roles, groups, and role-group combinations (memberships).
*
* @author Bonitasoft
* @since 1.0
*/
public final class ProfileUtis {
private static final Logger LOGGER = LoggerFactory.getLogger(ProfileUtis.class);
private static final int MAX_RESULTS = Integer.MAX_VALUE;
/**
* Private constructor to prevent instantiation.
*/
private ProfileUtis() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
/**
* Retrieves all unique user IDs associated with a given profile name.
* <p>
* This method collects users from all membership types:
* <ul>
* <li><b>USER</b>: Users directly assigned to the profile</li>
* <li><b>ROLE</b>: Users belonging to roles assigned to the profile</li>
* <li><b>GROUP</b>: Users belonging to groups assigned to the profile</li>
* <li><b>MEMBERSHIP</b>: Users with specific role-group combinations assigned to the profile</li>
* </ul>
*
* @param apiAccessor The Bonita API accessor to obtain ProfileAPI and IdentityAPI.
* @param profileName The name of the profile to search for (e.g., "Administrator", "User").
* @return A list of unique user IDs associated with the profile, or an empty list if
* the profile is not found or an error occurs.
* @throws IllegalArgumentException if apiAccessor is null or profileName is null/empty.
*/
public static List<Long> getUserIdsInProfile(APIAccessor apiAccessor, String profileName) {
validateInputs(apiAccessor, profileName);
long startTime = System.currentTimeMillis();
LOGGER.info("Starting user ID retrieval for profile: '{}'", profileName);
try {
ProfileAPI profileAPI = apiAccessor.getProfileAPI();
IdentityAPI identityAPI = apiAccessor.getIdentityAPI();
Optional<Profile> profileOpt = findProfileByName(profileAPI, profileName);
if (profileOpt.isEmpty()) {
LOGGER.warn("Profile '{}' not found. Returning empty list.", profileName);
return List.of();
}
long profileId = profileOpt.get().getId();
Set<Long> userIds = Stream.of(
collectDirectUsers(profileAPI, profileId),
collectUsersFromRoles(profileAPI, identityAPI, profileId),
collectUsersFromGroups(profileAPI, identityAPI, profileId),
collectUsersFromMemberships(profileAPI, identityAPI, profileId)
)
.flatMap(Set::stream)
.collect(Collectors.toSet());
LOGGER.info("Successfully retrieved {} unique user IDs for profile '{}' in {} ms",
userIds.size(), profileName, System.currentTimeMillis() - startTime);
return new ArrayList<>(userIds);
} catch (Exception e) {
LOGGER.error("Unexpected error retrieving user IDs for profile '{}': {}",
profileName, e.getMessage(), e);
return List.of();
}
}
/**
* Validates input parameters.
*
* @param apiAccessor The API accessor to validate.
* @param profileName The profile name to validate.
* @throws IllegalArgumentException if any parameter is invalid.
*/
private static void validateInputs(APIAccessor apiAccessor, String profileName) {
if (apiAccessor == null) {
throw new IllegalArgumentException("APIAccessor cannot be null");
}
if (profileName == null || profileName.isBlank()) {
throw new IllegalArgumentException("Profile name cannot be null or blank");
}
}
/**
* Finds a profile by its name.
*
* @param profileAPI The Profile API instance.
* @param profileName The name of the profile to find.
* @return An Optional containing the Profile if found, or empty if not found.
*/
private static Optional<Profile> findProfileByName(ProfileAPI profileAPI, String profileName) {
try {
SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, 1);
searchBuilder.filter(ProfileSearchDescriptor.NAME, profileName);
SearchResult<Profile> searchResult = profileAPI.searchProfiles(searchBuilder.done());
return searchResult.getResult().stream().findFirst();
} catch (Exception e) {
LOGGER.error("Error searching for profile '{}': {}", profileName, e.getMessage(), e);
return Optional.empty();
}
}
/**
* Collects user IDs from direct user memberships in the profile.
*
* @param profileAPI The Profile API instance.
* @param profileId The profile ID to search.
* @return A set of user IDs directly assigned to the profile.
*/
private static Set<Long> collectDirectUsers(ProfileAPI profileAPI, long profileId) {
try {
List<ProfileMember> members = searchProfileMembers(profileAPI, profileId, MemberType.USER);
Set<Long> userIds = members.stream()
.mapToLong(ProfileMember::getUserId)
.filter(id -> id > 0)
.boxed()
.collect(Collectors.toSet());
LOGGER.debug("Collected {} direct users from profile ID {}", userIds.size(), profileId);
return userIds;
} catch (Exception e) {
LOGGER.error("Error collecting direct users for profile ID {}: {}", profileId, e.getMessage(), e);
return Set.of();
}
}
/**
* Collects user IDs from role memberships in the profile.
*
* @param profileAPI The Profile API instance.
* @param identityAPI The Identity API instance.
* @param profileId The profile ID to search.
* @return A set of user IDs belonging to roles assigned to the profile.
*/
private static Set<Long> collectUsersFromRoles(ProfileAPI profileAPI, IdentityAPI identityAPI, long profileId) {
try {
List<ProfileMember> members = searchProfileMembers(profileAPI, profileId, MemberType.ROLE);
Set<Long> userIds = members.stream()
.mapToLong(ProfileMember::getRoleId)
.filter(roleId -> roleId > 0)
.boxed()
.flatMap(roleId -> getActiveUsersInRole(identityAPI, roleId).stream())
.map(User::getId)
.collect(Collectors.toSet());
LOGGER.debug("Collected {} users from {} role memberships for profile ID {}",
userIds.size(), members.size(), profileId);
return userIds;
} catch (Exception e) {
LOGGER.error("Error collecting users from roles for profile ID {}: {}", profileId, e.getMessage(), e);
return Set.of();
}
}
/**
* Collects user IDs from group memberships in the profile.
*
* @param profileAPI The Profile API instance.
* @param identityAPI The Identity API instance.
* @param profileId The profile ID to search.
* @return A set of user IDs belonging to groups assigned to the profile.
*/
private static Set<Long> collectUsersFromGroups(ProfileAPI profileAPI, IdentityAPI identityAPI, long profileId) {
try {
List<ProfileMember> members = searchProfileMembers(profileAPI, profileId, MemberType.GROUP);
Set<Long> userIds = members.stream()
.mapToLong(ProfileMember::getGroupId)
.filter(groupId -> groupId > 0)
.boxed()
.flatMap(groupId -> getActiveUsersInGroup(identityAPI, groupId).stream())
.map(User::getId)
.collect(Collectors.toSet());
LOGGER.debug("Collected {} users from {} group memberships for profile ID {}",
userIds.size(), members.size(), profileId);
return userIds;
} catch (Exception e) {
LOGGER.error("Error collecting users from groups for profile ID {}: {}", profileId, e.getMessage(), e);
return Set.of();
}
}
/**
* Collects user IDs from role-group (membership) combinations in the profile.
*
* @param profileAPI The Profile API instance.
* @param identityAPI The Identity API instance.
* @param profileId The profile ID to search.
* @return A set of user IDs matching the role-group combinations assigned to the profile.
*/
private static Set<Long> collectUsersFromMemberships(ProfileAPI profileAPI, IdentityAPI identityAPI, long profileId) {
try {
List<ProfileMember> members = searchProfileMembers(profileAPI, profileId, MemberType.MEMBERSHIP);
Set<Long> userIds = members.stream()
.filter(member -> member.getGroupId() > 0 && member.getRoleId() > 0)
.flatMap(member -> searchUsersByGroupAndRole(identityAPI, member.getGroupId(), member.getRoleId()).stream())
.map(User::getId)
.collect(Collectors.toSet());
LOGGER.debug("Collected {} users from {} membership combinations for profile ID {}",
userIds.size(), members.size(), profileId);
return userIds;
} catch (Exception e) {
LOGGER.error("Error collecting users from memberships for profile ID {}: {}", profileId, e.getMessage(), e);
return Set.of();
}
}
/**
* Searches for profile members of a specific type.
*
* @param profileAPI The Profile API instance.
* @param profileId The profile ID to search.
* @param memberType The type of membership to search for.
* @return A list of ProfileMember objects matching the criteria.
*/
private static List<ProfileMember> searchProfileMembers(ProfileAPI profileAPI, long profileId, MemberType memberType) {
try {
SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, MAX_RESULTS);
searchBuilder.filter(ProfileMemberSearchDescriptor.PROFILE_ID, profileId);
SearchResult<ProfileMember> result = profileAPI.searchProfileMembers(
memberType.name(), searchBuilder.done());
return result.getResult();
} catch (Exception e) {
LOGGER.error("Error searching profile members of type {} for profile ID {}: {}",
memberType, profileId, e.getMessage(), e);
return List.of();
}
}
/**
* Gets active users in a specific role.
*
* @param identityAPI The Identity API instance.
* @param roleId The role ID to search.
* @return A list of active users in the role.
*/
private static List<User> getActiveUsersInRole(IdentityAPI identityAPI, long roleId) {
try {
return identityAPI.getActiveUsersInRole(roleId, 0, MAX_RESULTS, UserCriterion.USER_NAME_ASC);
} catch (Exception e) {
LOGGER.error("Error getting active users in role {}: {}", roleId, e.getMessage(), e);
return List.of();
}
}
/**
* Gets active users in a specific group.
*
* @param identityAPI The Identity API instance.
* @param groupId The group ID to search.
* @return A list of active users in the group.
*/
private static List<User> getActiveUsersInGroup(IdentityAPI identityAPI, long groupId) {
try {
return identityAPI.getActiveUsersInGroup(groupId, 0, MAX_RESULTS, UserCriterion.USER_NAME_ASC);
} catch (Exception e) {
LOGGER.error("Error getting active users in group {}: {}", groupId, e.getMessage(), e);
return List.of();
}
}
/**
* Searches for users that belong to a specific group and role combination.
*
* @param identityAPI The Identity API instance.
* @param groupId The group ID to filter by.
* @param roleId The role ID to filter by.
* @return A list of users matching the criteria.
*/
private static List<User> searchUsersByGroupAndRole(IdentityAPI identityAPI, long groupId, long roleId) {
try {
SearchOptionsBuilder searchBuilder = new SearchOptionsBuilder(0, MAX_RESULTS);
searchBuilder.filter(UserSearchDescriptor.GROUP_ID, groupId);
searchBuilder.filter(UserSearchDescriptor.ROLE_ID, roleId);
SearchResult<User> result = identityAPI.searchUsers(searchBuilder.done());
return result.getResult();
} catch (Exception e) {
LOGGER.error("Error searching users by group {} and role {}: {}", groupId, roleId, e.getMessage(), e);
return List.of();
}
}
}