opendj3/opendj-rest2ldap-servlet/src/main/webapp/opendj-rest2ldap-servlet.json
@@ -4,9 +4,40 @@ "ldapConnectionFactories" : { // Unauthenticated connections used for performing bind requests. "default" : { "connectionPoolSize" : 10, // Indicates whether or not LDAP connections should be secured using // SSL or StartTLS. Acceptable values are: // // "none" - use plain LDAP connections (default) // "ssl" - secure connection using LDAPS // "startTLS" - secure connection using LDAP+StartTLS // "connectionSecurity" : "none", // Specifies the policy for trusting server certificates exchanged // during SSL/StartTLS negotiation. This setting and the following // trust policy settings will be ignored if there is no connection // security. Acceptable values are: // // "trustAll" - blindly trust all server certificates (default) // "jvm" - only certificates signed by the authorities // associated with the host JVM will be accepted // "file" - use a file-based trust store for validating // certificates. This option requires the following // "fileBasedTrustManager*" settings to be configured. // "trustManager" : "trustAll", // File based trust manager configuration (see above). "fileBasedTrustManagerType" : "JKS", "fileBasedTrustManagerFile" : "/path/to/truststore", "fileBasedTrustManagerPassword" : "password", // Re-usable pool of 24 connections per server. "connectionPoolSize" : 24, // Check pooled connections are alive every 30 seconds. "heartBeatIntervalSeconds" : 30, // The preferred load-balancing pool. "primaryLDAPServers" : [ { @@ -19,13 +50,13 @@ // Empty. ] }, // Authenticated connections which will be used for searches during // authentication and proxied operations (if enabled). This factory // will re-use the server "default" configuration. "root" : { "inheritFrom" : "default", // Defines how authentication should be performed. Only "simple" // authentication is supported at the moment. "authentication" : { @@ -36,7 +67,7 @@ } } }, // The Rest2LDAP authentication filter configuration. The filter will be // disabled if the configuration is not present. Upon successful // authentication the filter will create a security context containing the @@ -47,21 +78,21 @@ "authenticationFilter" : { // Indicates whether the filter should allow HTTP BASIC authentication. "supportHTTPBasicAuthentication" : true, // Indicates whether the filter should allow alternative authentication // and, if so, which HTTP headers it should obtain the username and // password from. "supportAltAuthentication" : true, "supportAltAuthentication" : true, "altAuthenticationUsernameHeader" : "X-OpenIDM-Username", "altAuthenticationPasswordHeader" : "X-OpenIDM-Password", // Indicates whether the authenticated LDAP connection should be cached // for use within the Rest2LDAP Servlet for subsequent LDAP operations. // If this is set to true then the Servlet will not need its own LDAP // connection factory and will also not need to use proxied // authorization. "reuseAuthenticatedConnection" : true, // Specifies how LDAP authentications should be performed. The method // must be one of: // @@ -74,29 +105,29 @@ // substituting the username into the // "searchFilterTemplate" using %s substitution. "method" : "search-simple", // The connection factory which will be exclusively used for // authenticating users using LDAP bind operations. "bindLDAPConnectionFactory" : "default", // The SASL AuthzID template which will be used for "sasl-plain" // authentication. The %s format parameters will be substituted with // the client-provided username, using DN character escaping for DN // AuthzIDs. "saslAuthzIdTemplate" : "dn:uid=%s,ou=people,dc=example,dc=com", // The connection factory which will be used for performing LDAP // searches to locate users when "search-simple" authentication is // enabled. "searchLDAPConnectionFactory" : "root", // The search parameters to use for "search-simple" authentication. The // %s filter format parameters will be substituted with the // client-provided username, using LDAP filter string character escaping. "searchBaseDN" : "ou=people,dc=example,dc=com", "searchScope" : "sub", // Or "one". "searchFilterTemplate" : "(&(objectClass=inetOrgPerson)(uid=%s))" // TODO: support for HTTP sessions? }, @@ -110,7 +141,7 @@ // be passed on from the filter, which may not always be the case // if the filter is configured to use HTTP sessions. "ldapConnectionFactory" : "root", // Specifies how LDAP authorization should be performed. The method // must be one of: // @@ -125,12 +156,12 @@ // authorization will only be used if there is no // pre-authenticated connection available. "authorizationPolicy" : "proxy", // The AuthzID template which will be used for proxied authorization. // The template should contain fields which are expected to be found in // the security context create during authentication, e.g. "dn" and "id". "proxyAuthzIdTemplate" : "dn:{dn}", // The REST APIs and their LDAP attribute mappings. "mappings" : { "/users" : { opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -21,6 +21,8 @@ import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.CONTROLS; import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull; import java.io.IOException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; @@ -52,13 +54,16 @@ import org.forgerock.opendj.ldap.FailoverLoadBalancingAlgorithm; import org.forgerock.opendj.ldap.Filter; import org.forgerock.opendj.ldap.LDAPConnectionFactory; import org.forgerock.opendj.ldap.LDAPOptions; import org.forgerock.opendj.ldap.LinkedAttribute; import org.forgerock.opendj.ldap.MultipleEntriesFoundException; import org.forgerock.opendj.ldap.RDN; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm; import org.forgerock.opendj.ldap.SSLContextBuilder; import org.forgerock.opendj.ldap.SearchScope; import org.forgerock.opendj.ldap.TimeoutResultException; import org.forgerock.opendj.ldap.TrustManagers; import org.forgerock.opendj.ldap.requests.BindRequest; import org.forgerock.opendj.ldap.requests.Requests; import org.forgerock.opendj.ldap.requests.SearchRequest; @@ -70,6 +75,23 @@ * collections. */ public final class Rest2LDAP { /** * Indicates whether or not LDAP client connections should use SSL or * StartTLS. */ private enum ConnectionSecurity { NONE, SSL, STARTTLS } /** * Specifies the mechanism which should be used for trusting certificates * presented by the LDAP server. */ private enum TrustManagerType { TRUSTALL, JVM, FILE } /** * A builder for incrementally constructing LDAP resource collections. */ @@ -965,6 +987,48 @@ bindRequest = null; } // Parse SSL/StartTLS parameters. final ConnectionSecurity connectionSecurity = configuration.get("connectionSecurity").defaultTo(ConnectionSecurity.NONE).asEnum( ConnectionSecurity.class); final LDAPOptions options = new LDAPOptions(); if (connectionSecurity != ConnectionSecurity.NONE) { try { // Configure SSL. final SSLContextBuilder builder = new SSLContextBuilder(); // Parse trust store configuration. final TrustManagerType trustManagerType = configuration.get("trustManager").defaultTo(TrustManagerType.TRUSTALL) .asEnum(TrustManagerType.class); switch (trustManagerType) { case TRUSTALL: builder.setTrustManager(TrustManagers.trustAll()); break; case JVM: // Do nothing: JVM trust manager is the default. break; case FILE: final String fileName = configuration.get("fileBasedTrustManagerFile").required().asString(); final String password = configuration.get("fileBasedTrustManagerPassword").asString(); final String type = configuration.get("fileBasedTrustManagerType").asString(); builder.setTrustManager(TrustManagers.checkUsingTrustStore(fileName, password != null ? password.toCharArray() : null, type)); break; } options.setSSLContext(builder.getSSLContext()); options.setUseStartTLS(connectionSecurity == ConnectionSecurity.STARTTLS); } catch (GeneralSecurityException e) { // Rethrow as unchecked exception. throw new IllegalArgumentException(e); } catch (IOException e) { // Rethrow as unchecked exception. throw new IllegalArgumentException(e); } } // Parse primary data center. final JsonValue primaryLDAPServers = configuration.get("primaryLDAPServers"); if (!primaryLDAPServers.isList() || primaryLDAPServers.size() == 0) { @@ -972,7 +1036,7 @@ } final ConnectionFactory primary = parseLDAPServers(primaryLDAPServers, bindRequest, connectionPoolSize, heartBeatIntervalSeconds); heartBeatIntervalSeconds, options); // Parse secondary data center(s). final JsonValue secondaryLDAPServers = configuration.get("secondaryLDAPServers"); @@ -981,7 +1045,7 @@ if (secondaryLDAPServers.size() > 0) { secondary = parseLDAPServers(secondaryLDAPServers, bindRequest, connectionPoolSize, heartBeatIntervalSeconds); heartBeatIntervalSeconds, options); } else { secondary = null; } @@ -1029,12 +1093,12 @@ private static ConnectionFactory parseLDAPServers(final JsonValue config, final BindRequest bindRequest, final int connectionPoolSize, final int heartBeatIntervalSeconds) { final int heartBeatIntervalSeconds, final LDAPOptions options) { final List<ConnectionFactory> servers = new ArrayList<ConnectionFactory>(config.size()); for (final JsonValue server : config) { final String host = server.get("hostname").required().asString(); final int port = server.get("port").required().asInteger(); ConnectionFactory factory = new LDAPConnectionFactory(host, port); ConnectionFactory factory = new LDAPConnectionFactory(host, port, options); if (bindRequest != null) { factory = Connections.newAuthenticatedConnectionFactory(factory, bindRequest); }