package io.lumify.ldap; import io.lumify.core.exception.LumifyException; import com.google.inject.Singleton; import com.unboundid.ldap.sdk.*; import com.unboundid.util.ssl.SSLUtil; import com.unboundid.util.ssl.TrustStoreTrustManager; import io.lumify.core.util.LumifyLogger; import io.lumify.core.util.LumifyLoggerFactory; import org.apache.commons.lang.text.StrSubstitutor; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.net.ssl.SSLSocketFactory; import java.security.GeneralSecurityException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.*; @Singleton public class LdapSearchServiceImpl implements LdapSearchService { private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(LdapSearchServiceImpl.class); private LDAPConnectionPool pool; private LdapSearchConfiguration ldapSearchConfiguration; public LdapSearchServiceImpl(LdapServerConfiguration serverConfig, LdapSearchConfiguration searchConfig) throws GeneralSecurityException, LDAPException { TrustStoreTrustManager tsManager = new TrustStoreTrustManager( serverConfig.getTrustStore(), serverConfig.getTrustStorePassword().toCharArray(), serverConfig.getTrustStoreType(), true ); SSLUtil sslUtil = new SSLUtil(tsManager); SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory(); if (serverConfig.getFailoverLdapServerHostname() != null) { String[] addresses = {serverConfig.getPrimaryLdapServerHostname(), serverConfig.getFailoverLdapServerHostname()}; int[] ports = {serverConfig.getPrimaryLdapServerPort(), serverConfig.getFailoverLdapServerPort()}; FailoverServerSet failoverSet = new FailoverServerSet(addresses, ports, socketFactory); SimpleBindRequest bindRequest = new SimpleBindRequest(serverConfig.getBindDn(), serverConfig.getBindPassword()); pool = new LDAPConnectionPool(failoverSet, bindRequest, serverConfig.getMaxConnections()); } else { LDAPConnection ldapConnection = new LDAPConnection( socketFactory, serverConfig.getPrimaryLdapServerHostname(), serverConfig.getPrimaryLdapServerPort(), serverConfig.getBindDn(), serverConfig.getBindPassword() ); pool = new LDAPConnectionPool(ldapConnection, serverConfig.getMaxConnections()); } Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if (!pool.isClosed()) { LOGGER.info("closing ldap connection pool"); pool.close(); } } }); ldapSearchConfiguration = searchConfig; } @Override public SearchResultEntry searchPeople(X509Certificate certificate) { Filter filter = buildPeopleSearchFilter(certificate); List<String> attributeNames = new ArrayList<String>(ldapSearchConfiguration.getUserAttributes()); if (certificate != null) { attributeNames.add(ldapSearchConfiguration.getUserCertificateAttribute()); } SearchResult results; try { results = pool.search( ldapSearchConfiguration.getUserSearchBase(), ldapSearchConfiguration.getUserSearchScope(), filter, attributeNames.toArray(new String[attributeNames.size()]) ); } catch (LDAPSearchException lse) { if (lse.getResultCode() == ResultCode.NO_SUCH_OBJECT) { throw new LumifyException("no results for LDAP search: " + filter, lse); } throw new LumifyException("search failed", lse); } if (results.getEntryCount() == 0) { throw new LumifyException("no results for LDAP search: " + filter); } if (certificate != null) { return getMatchingSearchResultEntry(certificate, filter, results); } else { if (results.getEntryCount() > 1) { throw new LumifyException("certificate matching not requested and more than one result for LDAP search: " + filter); } return results.getSearchEntries().get(0); } } @Override public Set<String> searchGroups(SearchResultEntry personEntry) { Map<String, String> subs = new HashMap<String, String>(); subs.put("dn", personEntry.getDN()); for (Attribute attr : personEntry.getAttributes()) { subs.put(attr.getName(), attr.getValue()); } try { StrSubstitutor sub = new StrSubstitutor(subs); String filterStr = sub.replace(ldapSearchConfiguration.getGroupSearchFilter()); Filter filter = Filter.create(filterStr); SearchResult results = pool.search( ldapSearchConfiguration.getGroupSearchBase(), ldapSearchConfiguration.getGroupSearchScope(), filter, ldapSearchConfiguration.getGroupNameAttribute() ); Set<String> groupNames = new HashSet<String>(); for (SearchResultEntry entry : results.getSearchEntries()) { if (entry.hasAttribute(ldapSearchConfiguration.getGroupNameAttribute())) { groupNames.add(entry.getAttributeValue(ldapSearchConfiguration.getGroupNameAttribute())); } } return groupNames; } catch (LDAPSearchException e) { throw new LumifyException("search failed", e); } catch (LDAPException e) { throw new LumifyException("Could not create filter", e); } } private Filter buildPeopleSearchFilter(X509Certificate certificate) { String certSubjectName = certificate.getSubjectX500Principal().getName(); try { LdapName ldapDN = new LdapName(certSubjectName); Map<String, Object> subs = new HashMap<String, Object>(); for (Rdn rdn : ldapDN.getRdns()) { subs.put(rdn.getType().toLowerCase(), rdn.getValue()); } StrSubstitutor sub = new StrSubstitutor(subs); String filterStr = sub.replace(ldapSearchConfiguration.getUserSearchFilter()); return Filter.create(filterStr); } catch (InvalidNameException e) { throw new LumifyException("invalid certificate subject name: " + certSubjectName, e); } catch (LDAPException e) { throw new LumifyException("Could not create filter", e); } } private SearchResultEntry getMatchingSearchResultEntry(X509Certificate certificate, Filter filter, SearchResult results) { byte[] encodedCert = new byte[0]; try { encodedCert = certificate.getEncoded(); } catch (CertificateEncodingException e) { throw new LumifyException("unable to get encoded version of user certificate", e); } for (SearchResultEntry entry : results.getSearchEntries()) { byte[][] entryCertificates = entry.getAttributeValueByteArrays(ldapSearchConfiguration.getUserCertificateAttribute()); for (byte[] entryCertificate : entryCertificates) { if (Arrays.equals(entryCertificate, encodedCert)) { return entry; } } } throw new LumifyException("no results with matching certificate for LDAP search: " + filter); } }