/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.auth;
import java.util.Arrays;
import java.util.Iterator;
import org.ldaptive.Connection;
import org.ldaptive.DerefAliases;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.ReturnAttributes;
import org.ldaptive.SearchFilter;
import org.ldaptive.SearchOperation;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResult;
import org.ldaptive.SearchScope;
import org.ldaptive.referral.ReferralHandler;
/**
* Base implementation for search dn resolvers.
*
* @author Middleware Services
*/
public abstract class AbstractSearchDnResolver extends AbstractSearchOperationFactory implements DnResolver
{
/** DN to search. */
private String baseDn = "";
/** Filter for searching for the user. */
private String userFilter;
/** Filter parameters for searching for the user. */
private Object[] userFilterParameters;
/** Whether to throw an exception if multiple DNs are found. */
private boolean allowMultipleDns;
/** Whether to use a subtree search when resolving DNs. */
private boolean subtreeSearch;
/** How to handle aliases. */
private DerefAliases derefAliases;
/** Referral handler. */
private ReferralHandler referralHandler;
/**
* Returns the base DN.
*
* @return base DN
*/
public String getBaseDn()
{
return baseDn;
}
/**
* Sets the base DN.
*
* @param dn base DN
*/
public void setBaseDn(final String dn)
{
logger.trace("setting baseDn: {}", dn);
baseDn = dn;
}
/**
* Returns the filter used to search for the user.
*
* @return filter for searching
*/
public String getUserFilter()
{
return userFilter;
}
/**
* Sets the filter used to search for the user.
*
* @param filter for searching
*/
public void setUserFilter(final String filter)
{
logger.trace("setting userFilter: {}", filter);
userFilter = filter;
}
/**
* Returns the filter parameters used to search for the user.
*
* @return filter parameters
*/
public Object[] getUserFilterParameters()
{
return userFilterParameters;
}
/**
* Sets the filter parameters used to search for the user.
*
* @param filterParams filter parameters
*/
public void setUserFilterParameters(final Object[] filterParams)
{
logger.trace("setting userFilterParameters: {}", Arrays.toString(filterParams));
userFilterParameters = filterParams;
}
/**
* Returns whether DN resolution should fail if multiple DNs are found.
*
* @return whether an exception will be thrown if multiple DNs are found
*/
public boolean getAllowMultipleDns()
{
return allowMultipleDns;
}
/**
* Sets whether DN resolution should fail if multiple DNs are found. If false an exception will be thrown if {@link
* #resolve(User)} finds more than one DN matching it's filter. Otherwise the first DN found is returned.
*
* @param b whether multiple DNs are allowed
*/
public void setAllowMultipleDns(final boolean b)
{
logger.trace("setting allowMultipleDns: {}", b);
allowMultipleDns = b;
}
/**
* Returns whether subtree searching will be used.
*
* @return whether the DN will be searched for over the entire base
*/
public boolean getSubtreeSearch()
{
return subtreeSearch;
}
/**
* Sets whether subtree searching will be used. If true, the DN used for authenticating will be searched for over the
* entire {@link #getBaseDn()}. Otherwise the DN will be searched for in the {@link #getBaseDn()} context.
*
* @param b whether the DN will be searched for over the entire base
*/
public void setSubtreeSearch(final boolean b)
{
logger.trace("setting subtreeSearch: {}", b);
subtreeSearch = b;
}
/**
* Returns how to dereference aliases.
*
* @return how to dereference aliases
*/
public DerefAliases getDerefAliases()
{
return derefAliases;
}
/**
* Sets how to dereference aliases.
*
* @param da how to dereference aliases
*/
public void setDerefAliases(final DerefAliases da)
{
logger.trace("setting derefAliases: {}", da);
derefAliases = da;
}
/**
* Returns the referral handler.
*
* @return referral handler
*/
public ReferralHandler getReferralHandler()
{
return referralHandler;
}
/**
* Sets the referral handler.
*
* @param handler referral handler
*/
public void setReferralHandler(final ReferralHandler handler)
{
logger.trace("setting referralHandler: {}", handler);
referralHandler = handler;
}
/**
* Attempts to find the DN for the supplied user. {@link #createSearchFilter(User)} ()} is used to create the search
* filter. If more than one entry matches the search, the result is controlled by {@link
* #setAllowMultipleDns(boolean)}.
*
* @param user to find DN for
*
* @return user DN
*
* @throws LdapException if the entry resolution fails
*/
@Override
public String resolve(final User user)
throws LdapException
{
logger.debug("resolve user={}", user);
String dn = null;
if (user != null) {
// create the search filter
final SearchFilter filter = createSearchFilter(user);
if (filter != null && filter.getFilter() != null) {
final SearchResult result = performLdapSearch(filter);
final Iterator<LdapEntry> answer = result.getEntries().iterator();
// return first match, otherwise user doesn't exist
if (answer != null && answer.hasNext()) {
dn = resolveDn(answer.next());
if (answer.hasNext()) {
logger.debug("multiple results found for user={} using filter={}", user, filter);
if (!allowMultipleDns) {
throw new LdapException("Found more than (1) DN for: " + user);
}
}
} else {
logger.info("search for user={} failed using filter={}", user, filter);
}
} else {
logger.error("DN search filter not found, no search performed");
}
} else {
logger.warn("DN resolution cannot occur, user is null");
}
logger.debug("resolved dn={} for user={}", dn, user);
return dn;
}
/**
* Returns the DN for the supplied ldap entry.
*
* @param entry to retrieve the DN from
*
* @return dn
*/
protected String resolveDn(final LdapEntry entry)
{
return entry.getDn();
}
/**
* Returns a search filter using {@link #userFilter} and {@link #userFilterParameters}. The user parameter is injected
* as a named parameter of 'user'.
*
* @param user to resolve DN
*
* @return search filter
*/
protected SearchFilter createSearchFilter(final User user)
{
final SearchFilter filter = new SearchFilter();
if (user != null && user.getIdentifier() != null && !"".equals(user.getIdentifier())) {
if (userFilter != null) {
logger.debug("searching for DN using userFilter");
filter.setFilter(userFilter);
if (userFilterParameters != null) {
filter.setParameters(userFilterParameters);
}
// assign user as a named parameter
filter.setParameter("user", user.getIdentifier());
// assign context as a named parameter
filter.setParameter("context", user.getContext());
} else {
logger.error("Invalid userFilter, cannot be null or empty.");
}
} else {
logger.warn("Search filter cannot be created, user input was empty or null");
}
return filter;
}
/**
* Returns a search request for searching for a single entry in an LDAP, returning no attributes.
*
* @param filter to execute
*
* @return search request
*/
protected SearchRequest createSearchRequest(final SearchFilter filter)
{
final SearchRequest request = new SearchRequest();
request.setBaseDn(baseDn);
request.setSearchFilter(filter);
request.setReturnAttributes(ReturnAttributes.NONE.value());
if (subtreeSearch) {
request.setSearchScope(SearchScope.SUBTREE);
} else {
request.setSearchScope(SearchScope.ONELEVEL);
}
request.setDerefAliases(derefAliases);
request.setReferralHandler(referralHandler);
return request;
}
/**
* Executes the ldap search operation with the supplied filter.
*
* @param filter to execute
*
* @return ldap search result
*
* @throws LdapException if an error occurs
*/
protected SearchResult performLdapSearch(final SearchFilter filter)
throws LdapException
{
final SearchRequest request = createSearchRequest(filter);
try (Connection conn = getConnection()) {
final SearchOperation op = createSearchOperation(conn);
return op.execute(request).getResult();
}
}
/**
* Retrieve a connection that is ready for use.
*
* @return connection
*
* @throws LdapException if an error occurs opening the connection
*/
protected abstract Connection getConnection()
throws LdapException;
}