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()); } }