package org.apereo.cas.web.flow.client;
import com.google.common.base.Throwables;
import org.apache.commons.lang3.StringUtils;
import org.ldaptive.Connection;
import org.ldaptive.ConnectionFactory;
import org.ldaptive.LdapAttribute;
import org.ldaptive.LdapEntry;
import org.ldaptive.LdapException;
import org.ldaptive.Response;
import org.ldaptive.ResultCode;
import org.ldaptive.SearchOperation;
import org.ldaptive.SearchRequest;
import org.ldaptive.SearchResult;
import org.ldaptive.Operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Peek into an LDAP server and check for the existence of an attribute
* in order to target invocation of spnego.
*
* @author Misagh Moayyed
* @author Sean Baker
* @since 4.1
*/
public class LdapSpnegoKnownClientSystemsFilterAction extends BaseSpnegoKnownClientSystemsFilterAction {
private static final Logger LOGGER = LoggerFactory.getLogger(LdapSpnegoKnownClientSystemsFilterAction.class);
/**
* The must-have attribute name.
*/
private final String spnegoAttributeName;
private final ConnectionFactory connectionFactory;
private final SearchRequest searchRequest;
/**
* Instantiates a new action.
* @param ipsToCheckPattern the ips to check pattern
* @param alternativeRemoteHostAttribute the alternative remote host attribute
* @param dnsTimeout # of milliseconds to wait for a DNS request to return
* @param connectionFactory the connection factory
* @param searchRequest the search request
* @param spnegoAttributeName the certificate revocation list attribute name
*/
public LdapSpnegoKnownClientSystemsFilterAction(final String ipsToCheckPattern, final String alternativeRemoteHostAttribute,
final long dnsTimeout, final ConnectionFactory connectionFactory, final SearchRequest searchRequest,
final String spnegoAttributeName) {
super(ipsToCheckPattern, alternativeRemoteHostAttribute, dnsTimeout);
this.connectionFactory = connectionFactory;
this.spnegoAttributeName = spnegoAttributeName;
this.searchRequest = searchRequest;
}
/**
* Create and open a connection to ldap
* via the given config and provider.
*
* @return the connection
* @throws LdapException the ldap exception
*/
protected Connection createConnection() throws LdapException {
LOGGER.debug("Establishing a connection...");
final Connection connection = this.connectionFactory.getConnection();
connection.open();
return connection;
}
@Override
protected boolean shouldDoSpnego(final String remoteIp) {
if (StringUtils.isBlank(this.spnegoAttributeName)) {
LOGGER.warn("Ignoring Spnego. Attribute name is not configured");
return false;
}
if (this.connectionFactory == null) {
LOGGER.warn("Ignoring Spnego. LDAP connection factory is not configured");
return false;
}
if (this.searchRequest == null) {
LOGGER.warn("Ignoring Spnego. LDAP search request is not configured");
return false;
}
final boolean ipCheck = ipPatternCanBeChecked(remoteIp);
if (ipCheck && !ipPatternMatches(remoteIp)) {
return false;
}
LOGGER.debug("Attempting to locate attribute [{}] for [{}]", this.spnegoAttributeName, remoteIp);
return executeSearchForSpnegoAttribute(remoteIp);
}
@Override
protected String getRemoteHostName(final String remoteIp) {
if ("localhost".equalsIgnoreCase(remoteIp) || remoteIp.startsWith("127.")) {
return remoteIp;
}
return super.getRemoteHostName(remoteIp);
}
/**
* Searches the ldap instance for the attribute value.
*
* @param remoteIp the remote ip
* @return true/false
*/
protected boolean executeSearchForSpnegoAttribute(final String remoteIp) {
Connection connection = null;
final String remoteHostName = getRemoteHostName(remoteIp);
LOGGER.debug("Resolved remote hostname [{}] based on ip [{}]", remoteHostName, remoteIp);
try {
connection = createConnection();
final Operation searchOperation = new SearchOperation(connection);
this.searchRequest.getSearchFilter().setParameter(0, remoteHostName);
LOGGER.debug("Using search filter [{}] on baseDn [{}]",
this.searchRequest.getSearchFilter().format(),
this.searchRequest.getBaseDn());
final Response<SearchResult> searchResult = searchOperation.execute(this.searchRequest);
if (searchResult.getResultCode() == ResultCode.SUCCESS) {
return processSpnegoAttribute(searchResult);
}
throw new RuntimeException("Failed to establish a connection ldap. " + searchResult.getMessage());
} catch (final LdapException e) {
LOGGER.error(e.getMessage(), e);
throw Throwables.propagate(e);
} finally {
if (connection != null) {
connection.close();
}
}
}
/**
* Verify spnego attribute value.
*
* @param searchResult the search result
* @return true if attribute value exists and has a value
*/
protected boolean processSpnegoAttribute(final Response<SearchResult> searchResult) {
final SearchResult result = searchResult.getResult();
if (result == null || result.getEntries().isEmpty()) {
LOGGER.debug("Spnego attribute is not found in the search results");
return false;
}
final LdapEntry entry = result.getEntry();
final LdapAttribute attribute = entry.getAttribute(this.spnegoAttributeName);
LOGGER.debug("Spnego attribute [{}] found as [{}] for [{}]", attribute.getName(), attribute.getStringValue(), entry.getDn());
return verifySpnegoAttributeValue(attribute);
}
/**
* Verify spnego attribute value.
* This impl simply makes sure the attribute exists and has a value.
*
* @param attribute the ldap attribute
* @return true if available. false otherwise.
*/
protected boolean verifySpnegoAttributeValue(final LdapAttribute attribute) {
return attribute != null && StringUtils.isNotBlank(attribute.getStringValue());
}
}