package org.apereo.cas.util.ldap.uboundid; import com.google.common.base.Throwables; import com.unboundid.ldap.listener.InMemoryDirectoryServer; import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; import com.unboundid.ldap.listener.InMemoryListenerConfig; import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.schema.Schema; import com.unboundid.util.ssl.KeyStoreKeyManager; import com.unboundid.util.ssl.SSLUtil; import com.unboundid.util.ssl.TrustStoreTrustManager; import org.apache.commons.io.IOUtils; import org.apereo.cas.util.LdapTestUtils; import org.ldaptive.LdapEntry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import javax.annotation.PreDestroy; import java.io.Closeable; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Properties; /** * @author Misagh Moayyed * @since 4.1.0 */ public class InMemoryTestLdapDirectoryServer implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(InMemoryTestLdapDirectoryServer.class); private InMemoryDirectoryServer directoryServer; private Collection<LdapEntry> ldapEntries; /** * Instantiates a new Ldap directory server. * Parameters need to be streams so they can be read from JARs. */ public InMemoryTestLdapDirectoryServer(final InputStream properties, final InputStream ldifFile, final InputStream schemaFile) { try { LOGGER.debug("Loading properties..."); final Properties p = new Properties(); p.load(properties); final InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(p.getProperty("ldap.rootDn")); config.addAdditionalBindCredentials(p.getProperty("ldap.managerDn"), p.getProperty("ldap.managerPassword")); LOGGER.debug("Loading keystore file..."); final File keystoreFile = File.createTempFile("key", "store"); try (OutputStream outputStream = new FileOutputStream(keystoreFile)) { IOUtils.copy(new ClassPathResource("/ldapServerTrustStore").getInputStream(), outputStream); } final String serverKeyStorePath = keystoreFile.getCanonicalPath(); final SSLUtil serverSSLUtil = new SSLUtil( new KeyStoreKeyManager(serverKeyStorePath, "changeit".toCharArray()), new TrustStoreTrustManager(serverKeyStorePath)); final SSLUtil clientSSLUtil = new SSLUtil(new TrustStoreTrustManager(serverKeyStorePath)); LOGGER.debug("Loading LDAP listeners and ports..."); config.setListenerConfigs( InMemoryListenerConfig.createLDAPConfig("LDAP", // Listener name null, // Listen address. (null = listen on all interfaces) 1389, // Listen port (0 = automatically choose an available port) serverSSLUtil.createSSLSocketFactory()), // StartTLS factory InMemoryListenerConfig.createLDAPSConfig("LDAPS", // Listener name null, // Listen address. (null = listen on all interfaces) 1636, // Listen port (0 = automatically choose an available port) serverSSLUtil.createSSLServerSocketFactory(), // Server factory clientSSLUtil.createSSLSocketFactory())); // Client factory config.setEnforceSingleStructuralObjectClass(false); config.setEnforceAttributeSyntaxCompliance(true); config.setMaxConnections(-1); LOGGER.debug("Loading LDAP schema..."); final File file = File.createTempFile("ldap", "schema"); try (OutputStream outputStream = new FileOutputStream(file)) { IOUtils.copy(schemaFile, outputStream); } LOGGER.debug("Setting LDAP schema..."); final Schema s = Schema.mergeSchemas(Schema.getSchema(file)); config.setSchema(s); this.directoryServer = new InMemoryDirectoryServer(config); LOGGER.debug("Populating directory..."); LOGGER.debug("Loading LDIF file..."); final File ldif = File.createTempFile("ldiff", "file"); try (OutputStream outputStream = new FileOutputStream(ldif)) { IOUtils.copy(ldifFile, outputStream); } LOGGER.debug("Importing LDIF file..."); this.directoryServer.importFromLDIF(true, ldif.getCanonicalPath()); int retryCount = 5; while (retryCount > 0) { try { LOGGER.debug("Trying to restart LDAP server: attempt [{}]", retryCount); this.directoryServer.restartServer(); try (LDAPConnection c = getConnection()) { LOGGER.debug("Connected to [{}]:[{}]", c.getConnectedAddress(), c.getConnectedPort()); populateDefaultEntries(c); } retryCount = 0; } catch (final Throwable e) { Thread.sleep(2000); retryCount--; } } } catch (final Exception e) { throw Throwables.propagate(e); } } private void populateDefaultEntries(final LDAPConnection c) throws Exception { populateEntries(c, new ClassPathResource("ldif/users-groups.ldif").getInputStream()); } public void populateEntries(final InputStream rs) throws Exception { populateEntries(getConnection(), rs); } protected void populateEntries(final LDAPConnection c, final InputStream rs) throws Exception { this.ldapEntries = LdapTestUtils.readLdif(rs, getBaseDn()); LdapTestUtils.createLdapEntries(c, ldapEntries); populateEntriesInternal(c); } protected void populateEntriesInternal(final LDAPConnection c) { } public String getBaseDn() { return this.directoryServer.getBaseDNs().get(0).toNormalizedString(); } public Collection<LdapEntry> getLdapEntries() { return this.ldapEntries; } public LDAPConnection getConnection() throws LDAPException { return this.directoryServer.getConnection(); } @Override @PreDestroy public void close() { LOGGER.debug("Shutting down LDAP server..."); this.directoryServer.closeAllConnections(true); this.directoryServer.shutDown(true); LOGGER.debug("Shut down LDAP server."); } public boolean isAlive() { try { return getConnection() != null; } catch (final Throwable e) { return false; } } }