/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.auth;
import java.util.Arrays;
import java.util.Iterator;
import org.ldaptive.DerefAliases;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.SearchFilter;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResult;
import org.ldaptive.SearchScope;
import org.ldaptive.handler.SearchEntryHandler;
import org.ldaptive.referral.ReferralHandler;
/**
* Base implementation for search entry resolvers. Uses an object level search on the {@link
* AuthenticationCriteria#getDn()} if no {@link #userFilter} is configured. If a {@link #userFilter} is configured, then
* a search is executed using that filter.
*
* @author Middleware Services
*/
public abstract class AbstractSearchEntryResolver extends AbstractSearchOperationFactory implements EntryResolver
{
/** 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 entries are found. */
private boolean allowMultipleEntries;
/** Whether to use a subtree search when resolving DNs. */
private boolean subtreeSearch;
/** How to handle aliases. */
private DerefAliases derefAliases;
/** Referral handler. */
private ReferralHandler referralHandler;
/** Ldap entry handlers. */
private SearchEntryHandler[] entryHandlers;
/**
* 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 entry resolution should fail if multiple entries are found.
*
* @return whether an exception will be thrown if multiple entries are found
*/
public boolean getAllowMultipleEntries()
{
return allowMultipleEntries;
}
/**
* Sets whether entry resolution should fail if multiple entries are found. If false an exception will be thrown if
* {@link #resolve(AuthenticationCriteria, AuthenticationHandlerResponse)} finds more than one entry matching it's
* filter. Otherwise the first entry found is returned.
*
* @param b whether multiple entries are allowed
*/
public void setAllowMultipleEntries(final boolean b)
{
logger.trace("setting allowMultipleEntries: {}", b);
allowMultipleEntries = b;
}
/**
* Returns whether subtree searching will be used.
*
* @return whether the entry will be searched for over the entire base
*/
public boolean getSubtreeSearch()
{
return subtreeSearch;
}
/**
* Sets whether subtree searching will be used. If true, the entry will be searched for over the entire {@link
* #getBaseDn()}. Otherwise the entry will be searched for in the {@link #getBaseDn()} context.
*
* @param b whether the entry 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;
}
/**
* Returns the search entry handlers.
*
* @return search entry handlers
*/
public SearchEntryHandler[] getSearchEntryHandlers()
{
return entryHandlers;
}
/**
* Sets the search entry handlers.
*
* @param handlers search entry handlers
*/
public void setSearchEntryHandlers(final SearchEntryHandler... handlers)
{
entryHandlers = handlers;
}
/**
* Executes an ldap search with the supplied authentication criteria.
*
* @param criteria authentication criteria associated with the user
* @param response response from the authentication event
*
* @return search result
*
* @throws LdapException if an error occurs attempting the search
*/
protected abstract SearchResult performLdapSearch(
final AuthenticationCriteria criteria,
final AuthenticationHandlerResponse response)
throws LdapException;
/**
* Returns a search filter using {@link #userFilter} and {@link #userFilterParameters}. {@link
* User#getIdentifier()} is injected with a named parameter of 'user', {@link User#getContext()} is injected with a
* named parameter of 'context', and {@link AuthenticationCriteria#getDn()} is injected with a named parameter of
* 'dn'.
*
* @param ac authentication criteria
*
* @return search filter
*/
protected SearchFilter createSearchFilter(final AuthenticationCriteria ac)
{
final SearchFilter filter = new SearchFilter();
if (userFilter != null) {
logger.debug("searching for entry using userFilter");
filter.setFilter(userFilter);
if (userFilterParameters != null) {
filter.setParameters(userFilterParameters);
}
// assign named parameters
filter.setParameter("user", ac.getAuthenticationRequest().getUser().getIdentifier());
filter.setParameter("context", ac.getAuthenticationRequest().getUser().getContext());
filter.setParameter("dn", ac.getDn());
} else {
logger.error("Invalid userFilter, cannot be null or empty.");
}
return filter;
}
/**
* Returns a search request for the supplied authentication criteria. If no {@link #userFilter} is defined then an
* object level search on the authentication criteria DN is returned. Otherwise the {@link #userFilter}, {@link
* #baseDn} and {@link #subtreeSearch} are used to create the search request.
*
* @param ac authentication criteria containing a DN
*
* @return search request
*/
protected SearchRequest createSearchRequest(final AuthenticationCriteria ac)
{
SearchRequest request;
if (userFilter != null) {
request = new SearchRequest(baseDn, createSearchFilter(ac));
request.setReturnAttributes(ac.getAuthenticationRequest().getReturnAttributes());
if (subtreeSearch) {
request.setSearchScope(SearchScope.SUBTREE);
} else {
request.setSearchScope(SearchScope.ONELEVEL);
}
} else {
request = SearchRequest.newObjectScopeSearchRequest(
ac.getDn(),
ac.getAuthenticationRequest().getReturnAttributes());
}
request.setDerefAliases(derefAliases);
request.setReferralHandler(referralHandler);
request.setSearchEntryHandlers(entryHandlers);
return request;
}
@Override
public LdapEntry resolve(final AuthenticationCriteria criteria, final AuthenticationHandlerResponse response)
throws LdapException
{
logger.debug("resolve criteria={}", criteria);
final SearchResult result = performLdapSearch(criteria, response);
logger.debug("resolved result={} for criteria={}", result, criteria);
LdapEntry entry = null;
final Iterator<LdapEntry> answer = result.getEntries().iterator();
if (answer != null && answer.hasNext()) {
entry = answer.next();
if (answer.hasNext()) {
logger.debug("multiple results found for user={}", criteria.getDn());
if (!allowMultipleEntries) {
throw new LdapException("Found more than (1) entry for: " + criteria.getDn());
}
}
}
return entry;
}
}