package org.apereo.cas.authorization;
import org.apereo.cas.configuration.support.Beans;
import org.ldaptive.ConnectionFactory;
import org.ldaptive.LdapAttribute;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.Response;
import org.ldaptive.SearchExecutor;
import org.ldaptive.SearchResult;
import org.pac4j.core.authorization.generator.AuthorizationGenerator;
import org.pac4j.core.profile.CommonProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
* Provides a simple {@link AuthorizationGenerator} implementation that obtains user roles from an LDAP search.
* Two searches are performed by this component for every user details lookup:
* <ol>
* <li>Search for an entry to resolve the username. In most cases the search should return exactly one result,
* but the {@link #allowMultipleResults} property may be toggled to change that behavior.</li>
* <li>Search for groups of which the user is a member. This search commonly occurs on a separate directory
* branch than that of the user search.</li>
* </ol>
*
* @author Jerome Leleu
* @author Marvin S. Addison
* @author Misagh Moayyed
* @since 5.1.0
*/
public class LdapUserGroupsToRolesAuthorizationGenerator extends BaseUseAttributesAuthorizationGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(LdapUserGroupsToRolesAuthorizationGenerator.class);
private final String groupAttributeName;
private final String groupPrefix;
private final SearchExecutor groupSearchExecutor;
/**
* Instantiates a new Ldap user groups to roles authorization generator.
*
* @param factory the factory
* @param userSearchExecutor the user search executor
* @param allowMultipleResults the allow multiple results
* @param groupAttributeName the group attribute name
* @param groupPrefix the group prefix
* @param groupSearchExecutor the group search executor
*/
public LdapUserGroupsToRolesAuthorizationGenerator(final ConnectionFactory factory,
final SearchExecutor userSearchExecutor,
final boolean allowMultipleResults,
final String groupAttributeName,
final String groupPrefix,
final SearchExecutor groupSearchExecutor) {
super(factory, userSearchExecutor, allowMultipleResults);
this.groupAttributeName = groupAttributeName;
this.groupPrefix = groupPrefix;
this.groupSearchExecutor = groupSearchExecutor;
}
@Override
protected CommonProfile generateAuthorizationForLdapEntry(final CommonProfile profile, final LdapEntry userEntry) {
try {
LOGGER.debug("Attempting to get roles for user [{}].", userEntry.getDn());
final Response<SearchResult> response = this.groupSearchExecutor.search(
this.connectionFactory,
Beans.newLdaptiveSearchFilter(this.groupSearchExecutor.getSearchFilter().getFilter(),
Beans.LDAP_SEARCH_FILTER_DEFAULT_PARAM_NAME, Arrays.asList(userEntry.getDn())));
LOGGER.debug("LDAP role search response: [{}]", response);
final SearchResult groupResult = response.getResult();
for (final LdapEntry entry : groupResult.getEntries()) {
final LdapAttribute groupAttribute = entry.getAttribute(this.groupAttributeName);
if (groupAttribute == null) {
LOGGER.warn("Role attribute not found on entry [{}]", entry);
continue;
}
addProfileRolesFromAttributes(profile, groupAttribute, this.groupPrefix);
}
} catch (final LdapException e) {
throw new RuntimeException("LDAP error fetching roles for user.", e);
}
return profile;
}
}