mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Yannick Lecaillez
16.24.2016 f098b470ee98ecd478845cd0442a141e6a9b3a54
OPENDJ-3015: Oauth2 HTTP client must support HTTPS

* Add support for password file.
* Set JVM as default for TrustManager rather than trustAll.
* Use a common TrustManager & KeyManager
5 files modified
499 ■■■■ changed files
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/opendj-rest2ldap-config.json 313 ●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java 70 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java 105 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java 9 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties 2 ●●●●● patch | view | raw | blame | history
opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/opendj-rest2ldap-config.json
@@ -1,4 +1,42 @@
{
    "security": {
        // 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
        // "jvm"      - only certificates signed by the authorities
        //              associated with the host JVM will be accepted (default)
        // "file"     - use a file-based trust store for validating
        //              certificates. This option requires the following
        //              "fileBasedTrustManager*" settings to be configured.
        //
        "trustManager": "jvm",
        // File based trust manager configuration (see above).
        "fileBasedTrustManagerType": "JKS",
        "fileBasedTrustManagerFile": "/path/to/truststore",
        "fileBasedTrustManagerPasswordFile": "/path/to/pinfile",
        // Specifies the key-manager for authenticating the http client performing
        // the request against the access-token resolver endpoint. Acceptable values are:
        //
        // "jvm"      - use the JVM's default keystore for retrieving certificates. (default)
        // "keystore" - use the named key store file for retrieving certificates.
        // "pkcs11"   - use a PKCS#11 token for retrieving certificates.
        "keyManager": "jvm",
        // Keystore based key manager configuration (see above).
        "keyStoreFile": "/path/to/keystore",
        "keyStorePasswordFile": "/path/to/pinfile",
        "keyStoreFormat": "JKS",
        "keyStoreProvider": "",
        // PKCS11 based key manager configuration
        "pkcs11PasswordFile": "/path/to/pinfile"
    },
    // The array of connection factories which will be used by the Rest2LDAP
    // Servlet and authentication filter.
    "ldapConnectionFactories" : {
@@ -13,24 +51,9 @@
            //
            "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",
            // This alias points at an existing certificate that is used for SSL authentication for secure
            // communication between this gateway and the remote LDAP server.
            "sslCertAlias": "client-cert",
            // Re-usable pool of 24 connections per server.
            "connectionPoolSize"       : 24,
@@ -41,12 +64,10 @@
            "heartBeatTimeoutMilliSeconds" : 500,
            // The preferred load-balancing pool.
            "primaryLDAPServers"       : [
                {
            "primaryLDAPServers": [{
                    "hostname" : "localhost",
                    "port"     : 1389
                }
            ],
            }],
            // The fail-over load-balancing pool (optional).
            "secondaryLDAPServers"     : [
                // Empty.
@@ -180,6 +201,10 @@
                // This attribute is required and must have a string syntax.
                "endpointURL": "http://openam.example.com:8080/openam/oauth2/tokeninfo",
                // This alias points at an existing certificate that is used for SSL authentication for secure
                // communication between this gateway and the OpenAM access-token resolver.
                "sslCertAlias": "client-cert",
                // The default authzIdTemplate demonstrates how an authorization DN may be constructed
                // from the "uid" field in the following example OpenAM tokeninfo response:
                // {
@@ -201,6 +226,10 @@
                // This attribute is required and must have a string syntax.
                "endpointURL": "http://openam.example.com:8080/openam/oauth2/myrealm/introspect",
                // This alias points at an existing certificate that is used for SSL authentication for secure
                // communication between this gateway and the introspection access-token resolver.
                "sslCertAlias": "client-cert",
                // Token introspect endpoint requires authentication.
                // It should support HTTP basic authorization (a base64-encoded string of clientId:clientSecret)
                // These attributes are mandatory.
@@ -288,8 +317,7 @@
                "strategy"    : "clientDNNaming",
                "dnAttribute" : "uid"
            },
            "additionalLDAPAttributes" : [
                {
            "additionalLDAPAttributes": [{
                    "type" : "objectClass",
                    "values" : [
                        "top",
@@ -297,44 +325,134 @@
                        "organizationalPerson",
                        "inetOrgPerson"
                    ]
                }
            ],
            }],
            "attributes" : {
                "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
                "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
                "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
                "userName"    : { "simple"   : { "ldapAttribute" : "mail", "isSingleValued" : true, "writability" : "readOnly" } },
                "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true } },
                "name"        : { "object"   : {
                    "givenName"  : { "simple" : { "ldapAttribute" : "givenName", "isSingleValued" : true } },
                    "familyName" : { "simple" : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } }
                } },
                "manager"     : { "reference" : {
                "schemas": {
                    "constant": ["urn:scim:schemas:core:1.0"]
                },
                "_id": {
                    "simple": {
                        "ldapAttribute": "uid",
                        "isSingleValued": true,
                        "isRequired": true,
                        "writability": "createOnly"
                    }
                },
                "_rev": {
                    "simple": {
                        "ldapAttribute": "etag",
                        "isSingleValued": true,
                        "writability": "readOnly"
                    }
                },
                "userName": {
                    "simple": {
                        "ldapAttribute": "mail",
                        "isSingleValued": true,
                        "writability": "readOnly"
                    }
                },
                "displayName": {
                    "simple": {
                        "ldapAttribute": "cn",
                        "isSingleValued": true,
                        "isRequired": true
                    }
                },
                "name": {
                    "object": {
                        "givenName": {
                            "simple": {
                                "ldapAttribute": "givenName",
                                "isSingleValued": true
                            }
                        },
                        "familyName": {
                            "simple": {
                                "ldapAttribute": "sn",
                                "isSingleValued": true,
                                "isRequired": true
                            }
                        }
                    }
                },
                "manager": {
                    "reference": {
                    "ldapAttribute" : "manager",
                    "baseDN"        : "ou=people,dc=example,dc=com",
                    "primaryKey"    : "uid",
                    "mapper"         : { "object" : {
                        "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
                        "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
                    } }
                } },
                "groups"     : { "reference" : {
                        "mapper": {
                            "object": {
                                "_id": {
                                    "simple": {
                                        "ldapAttribute": "uid",
                                        "isSingleValued": true,
                                        "isRequired": true
                                    }
                                },
                                "displayName": {
                                    "simple": {
                                        "ldapAttribute": "cn",
                                        "isSingleValued": true,
                                        "writability": "readOnlyDiscardWrites"
                                    }
                                }
                            }
                        }
                    }
                },
                "groups": {
                    "reference": {
                    "ldapAttribute" : "isMemberOf",
                    "baseDN"        : "ou=groups,dc=example,dc=com",
                    "writability"   : "readOnly",
                    "primaryKey"    : "cn",
                    "mapper"        : { "object" : {
                        "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true } }
                    } }
                } },
                "contactInformation" : { "object" : {
                    "telephoneNumber" : { "simple" : { "ldapAttribute" : "telephoneNumber", "isSingleValued" : true } },
                    "emailAddress"    : { "simple" : { "ldapAttribute" : "mail", "isSingleValued" : true } }
                } },
                "meta"        : { "object" : {
                    "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
                    "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
                } }
                        "mapper": {
                            "object": {
                                "_id": {
                                    "simple": {
                                        "ldapAttribute": "cn",
                                        "isSingleValued": true
                                    }
                                }
                            }
                        }
                    }
                },
                "contactInformation": {
                    "object": {
                        "telephoneNumber": {
                            "simple": {
                                "ldapAttribute": "telephoneNumber",
                                "isSingleValued": true
                            }
                        },
                        "emailAddress": {
                            "simple": {
                                "ldapAttribute": "mail",
                                "isSingleValued": true
                            }
                        }
                    }
                },
                "meta": {
                    "object": {
                        "created": {
                            "simple": {
                                "ldapAttribute": "createTimestamp",
                                "isSingleValued": true,
                                "writability": "readOnly"
                            }
                        },
                        "lastModified": {
                            "simple": {
                                "ldapAttribute": "modifyTimestamp",
                                "isSingleValued": true,
                                "writability": "readOnly"
                            }
                        }
                    }
                }
            }
        },
        "/groups" : {
@@ -347,35 +465,84 @@
                "strategy"    : "clientDNNaming",
                "dnAttribute" : "cn"
            },
            "additionalLDAPAttributes" : [
                {
            "additionalLDAPAttributes": [{
                    "type" : "objectClass",
                    "values" : [
                        "top",
                        "groupOfUniqueNames"
                    ]
                }
            ],
            }],
            "attributes" : {
                "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
                "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
                "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
                "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "readOnly" } },
                "members"    : { "reference" : {
                "schemas": {
                    "constant": ["urn:scim:schemas:core:1.0"]
                },
                "_id": {
                    "simple": {
                        "ldapAttribute": "cn",
                        "isSingleValued": true,
                        "isRequired": true,
                        "writability": "createOnly"
                    }
                },
                "_rev": {
                    "simple": {
                        "ldapAttribute": "etag",
                        "isSingleValued": true,
                        "writability": "readOnly"
                    }
                },
                "displayName": {
                    "simple": {
                        "ldapAttribute": "cn",
                        "isSingleValued": true,
                        "isRequired": true,
                        "writability": "readOnly"
                    }
                },
                "members": {
                    "reference": {
                    "ldapAttribute" : "uniqueMember",
                    "baseDN"        : "dc=example,dc=com",
                    "primaryKey"    : "uid",
                    "mapper"        : { "object" : {
                        "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
                        "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
                    } }
                } },
                "meta"        : { "object" : {
                    "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
                    "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
                } }
                        "mapper": {
                            "object": {
                                "_id": {
                                    "simple": {
                                        "ldapAttribute": "uid",
                                        "isSingleValued": true,
                                        "isRequired": true
                                    }
                                },
                                "displayName": {
                                    "simple": {
                                        "ldapAttribute": "cn",
                                        "isSingleValued": true,
                                        "writability": "readOnlyDiscardWrites"
            }
        }
    }
}
                    }
                },
                "meta": {
                    "object": {
                        "created": {
                            "simple": {
                                "ldapAttribute": "createTimestamp",
                                "isSingleValued": true,
                                "writability": "readOnly"
                            }
                        },
                        "lastModified": {
                            "simple": {
                                "ldapAttribute": "modifyTimestamp",
                                "isSingleValued": true,
                                "writability": "readOnly"
                            }
                        }
                    }
                }
            }
        }
    }
}
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -31,8 +31,8 @@
import static org.forgerock.opendj.rest2ldap.Utils.newLocalizedIllegalArgumentException;
import static org.forgerock.opendj.rest2ldap.Utils.newJsonValueException;
import static org.forgerock.util.time.Duration.*;
import static org.forgerock.opendj.ldap.KeyManagers.useSingleCertificate;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -42,6 +42,9 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509KeyManager;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.CollectionResourceProvider;
import org.forgerock.json.resource.ResourceException;
@@ -68,7 +71,6 @@
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;
@@ -88,10 +90,18 @@
     * Specifies the mechanism which should be used for trusting certificates
     * presented by the LDAP server.
     */
    private enum TrustManagerType {
    enum TrustManagerType {
        TRUSTALL, JVM, FILE
    }
    /**
     * Specifies the mechanism which manage which X509 certificate-based key pairs should be used to authenticate the
     * local side of a secure socket.
     */
    enum KeyManagerType {
        JVM, KEYSTORE, PKCS11
    }
    /** A builder for incrementally constructing LDAP resource collections. */
    public static final class Builder {
        private final List<Attribute> additionalLDAPAttributes = new LinkedList<>();
@@ -784,6 +794,12 @@
     *            The JSON configuration.
     * @param name
     *            The name of the connection factory configuration to be parsed.
     * @param trustManager
     *            The trust manager to use for secure connection. Can be {@code null}
     *            to use the default JVM trust manager.
     * @param keyManager
     *            The key manager to use for secure connection. Can be {@code null}
     *            to use the default JVM key manager.
     * @param providerClassLoader
     *            The {@link ClassLoader} used to fetch the
     *            {@link org.forgerock.opendj.ldap.spi.TransportProvider}.
@@ -794,10 +810,12 @@
     */
    public static ConnectionFactory configureConnectionFactory(final JsonValue configuration,
                                                               final String name,
                                                               final TrustManager trustManager,
                                                               final X509KeyManager keyManager,
                                                               final ClassLoader providerClassLoader) {
        final JsonValue normalizedConfiguration =
                normalizeConnectionFactory(configuration, name, 0);
        return configureConnectionFactory(normalizedConfiguration, providerClassLoader);
        return configureConnectionFactory(normalizedConfiguration, trustManager, keyManager, providerClassLoader);
    }
    /**
@@ -809,13 +827,19 @@
     *            The JSON configuration.
     * @param name
     *            The name of the connection factory configuration to be parsed.
     * @param trustManager
     *            The trust manager to use for secure connection. Can be {@code null}
     *            to use the default JVM trust manager.
     * @param keyManager
     *            The key manager to use for secure connection. Can be {@code null}
     *            to use the default JVM key manager.
     * @return A new connection factory using the provided JSON configuration.
     * @throws IllegalArgumentException
     *             If the configuration is invalid.
     */
    public static ConnectionFactory configureConnectionFactory(final JsonValue configuration,
            final String name) {
        return configureConnectionFactory(configuration, name, null);
            final String name, final TrustManager trustManager, final X509KeyManager keyManager) {
        return configureConnectionFactory(configuration, name, trustManager, keyManager, null);
    }
    /**
@@ -908,6 +932,8 @@
    }
    private static ConnectionFactory configureConnectionFactory(final JsonValue configuration,
                                                                final TrustManager trustManager,
                                                                final X509KeyManager keyManager,
                                                                final ClassLoader providerClassLoader) {
        final long heartBeatIntervalSeconds = configuration.get("heartBeatIntervalSeconds").defaultTo(30L).asLong();
        final Duration heartBeatInterval = duration(Math.max(heartBeatIntervalSeconds, 1L), TimeUnit.SECONDS);
@@ -948,32 +974,14 @@
            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;
                }
                builder.setTrustManager(trustManager);
                final String sslCertAlias = configuration.get("sslCertAlias").asString();
                builder.setKeyManager(sslCertAlias != null
                        ? useSingleCertificate(sslCertAlias, keyManager)
                        : keyManager);
                options.set(SSL_CONTEXT, builder.getSSLContext());
                options.set(SSL_USE_STARTTLS,
                            connectionSecurity == ConnectionSecurity.STARTTLS);
            } catch (GeneralSecurityException | IOException e) {
                options.set(SSL_USE_STARTTLS, connectionSecurity == ConnectionSecurity.STARTTLS);
            } catch (GeneralSecurityException e) {
                // Rethrow as unchecked exception.
                throw new IllegalArgumentException(e);
            }
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
@@ -17,6 +17,10 @@
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.*;
import static org.forgerock.http.handler.HttpClientHandler.*;
import static org.forgerock.opendj.ldap.KeyManagers.*;
import static org.forgerock.opendj.ldap.TrustManagers.checkUsingTrustStore;
import static org.forgerock.opendj.ldap.TrustManagers.trustAll;
import static org.forgerock.http.util.Json.readJsonLenient;
import static org.forgerock.json.JsonValueFunctions.duration;
import static org.forgerock.json.JsonValueFunctions.enumConstant;
@@ -31,12 +35,14 @@
import static org.forgerock.util.Reject.checkNotNull;
import static org.forgerock.util.Utils.closeSilently;
import static org.forgerock.util.Utils.joinAsString;
import static org.forgerock.opendj.rest2ldap.Utils.readPasswordFromFile;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -46,6 +52,10 @@
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509KeyManager;
import org.forgerock.openig.oauth2.AccessTokenInfo;
import org.forgerock.openig.oauth2.AccessTokenException;
import org.forgerock.openig.oauth2.AccessTokenResolver;
@@ -71,11 +81,14 @@
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.rest2ldap.Rest2LDAP.KeyManagerType;
import org.forgerock.opendj.rest2ldap.Rest2LDAP.TrustManagerType;
import org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategy;
import org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.ConditionalFilter;
import org.forgerock.services.context.SecurityContext;
import org.forgerock.util.Factory;
import org.forgerock.util.Function;
import org.forgerock.util.Options;
import org.forgerock.util.Pair;
import org.forgerock.util.PerItemEvictionStrategyCache;
import org.forgerock.util.annotations.VisibleForTesting;
@@ -113,6 +126,9 @@
    /** Used for token caching. */
    private ScheduledExecutorService executorService;
    private TrustManager trustManager;
    private X509KeyManager keyManager;
    /** Define the method which should be used to resolve an OAuth2 access token. */
    private enum OAuth2ResolverType {
        RFC7662, OPENAM, CTS, FILE;
@@ -175,6 +191,7 @@
        try {
            final JsonValue configuration = readJson(configurationUrl);
            executorService = Executors.newSingleThreadScheduledExecutor();
            configureSecurity(configuration.get("security"));
            configureConnectionFactories(configuration.get("ldapConnectionFactories"));
            return Handlers.chainOf(
                    CrestHttp.newHttpHandler(configureRest2Ldap(configuration)),
@@ -204,10 +221,76 @@
        return router;
    }
    private void configureSecurity(final JsonValue configuration) {
        try {
            trustManager = configureTrustManager(configuration, TrustManagerType.JVM);
        } catch (GeneralSecurityException | IOException e) {
            throw new IllegalArgumentException(ERR_CONFIG_INVALID_TRUST_MANAGER
                    .get(configuration.getPointer(), e.getLocalizedMessage()).toString(), e);
        }
        try {
            keyManager = configureKeyManager(configuration, KeyManagerType.JVM);
        } catch (GeneralSecurityException | IOException e) {
            throw new IllegalArgumentException(ERR_CONFIG_INVALID_KEY_MANAGER
                    .get(configuration.getPointer(), e.getLocalizedMessage()).toString(), e);
        }
    }
    private TrustManager configureTrustManager(JsonValue config, TrustManagerType defaultIfMissing)
            throws GeneralSecurityException, IOException {
        // Parse trust store configuration.
        final TrustManagerType trustManagerType =
                config.get("trustManager").defaultTo(defaultIfMissing).as(enumConstant(TrustManagerType.class));
        switch (trustManagerType) {
        case TRUSTALL:
            return trustAll();
        case JVM:
            return null;
        case FILE:
            final String fileName = config.get("fileBasedTrustManagerFile").required().asString();
            final String passwordFile = config.get("fileBasedTrustManagerPasswordFile").asString();
            final String password = passwordFile != null
                    ? readPasswordFromFile(passwordFile)
                    : config.get("fileBasedTrustManagerPassword").asString();
            final String type = config.get("fileBasedTrustManagerType").asString();
            return checkUsingTrustStore(fileName, password != null ? password.toCharArray() : null, type);
        default:
            throw new IllegalArgumentException("Unsupported trust-manager type: " + trustManagerType);
        }
    }
    private X509KeyManager configureKeyManager(JsonValue config, KeyManagerType defaultIfMissing)
            throws GeneralSecurityException, IOException {
        // Parse trust store configuration.
        final KeyManagerType keyManagerType = config.get("keyManager").defaultTo(defaultIfMissing)
                .as(enumConstant(KeyManagerType.class));
        switch (keyManagerType) {
        case JVM:
            return useJvmDefaultKeyStore();
        case KEYSTORE:
            final String fileName = config.get("keyStoreFile").required().asString();
            final String passwordFile = config.get("keyStorePasswordFile").asString();
            final String password = passwordFile != null
                    ? readPasswordFromFile(passwordFile)
                    : config.get("keyStorePassword").asString();
            final String format = config.get("keyStoreFormat").asString();
            final String provider = config.get("keyStoreProvider").asString();
            return useKeyStoreFile(fileName, password != null ? password.toCharArray() : null, format, provider);
        case PKCS11:
            final String pkcs11PasswordFile = config.get("pkcs11PasswordFile").asString();
            return usePKCS11Token(pkcs11PasswordFile != null
                    ? readPasswordFromFile(pkcs11PasswordFile).toCharArray()
                    : null);
        default:
            throw new IllegalArgumentException("Unsupported key-manager type: " + keyManagerType);
        }
    }
    private void configureConnectionFactories(final JsonValue config) {
        connectionFactories.clear();
        for (String name : config.keys()) {
            connectionFactories.put(name, configureConnectionFactory(config, name));
            connectionFactories.put(name, configureConnectionFactory(config, name, trustManager, keyManager));
        }
    }
@@ -282,9 +365,10 @@
        case RFC7662:
            return parseRfc7662Resolver(configuration);
        case OPENAM:
            return new OpenAmAccessTokenResolver(new HttpClientHandler(),
            final JsonValue openAm = configuration.get("openam");
            return new OpenAmAccessTokenResolver(newHttpClientHandler(openAm),
                                                 TimeService.SYSTEM,
                                                 configuration.get("openam").get("endpointURL").required().asString());
                                                 openAm.get("endpointURL").required().asString());
        case CTS:
            final JsonValue cts = configuration.get("cts").required();
            return newCtsAccessTokenResolver(
@@ -303,7 +387,7 @@
        final JsonValue rfc7662 = configuration.get("rfc7662").required();
        final String introspectionEndPointURL = rfc7662.get("endpointURL").required().asString();
        try {
            return newRfc7662AccessTokenResolver(new HttpClientHandler(),
            return newRfc7662AccessTokenResolver(newHttpClientHandler(rfc7662),
                                                 new URI(introspectionEndPointURL),
                                                 rfc7662.get("clientId").required().asString(),
                                                 rfc7662.get("clientSecret").required().asString());
@@ -313,6 +397,19 @@
        }
    }
    private HttpClientHandler newHttpClientHandler(final JsonValue config) throws HttpApplicationException {
        final Options httpOptions = Options.defaultOptions();
        if (trustManager != null) {
            httpOptions.set(OPTION_TRUST_MANAGERS, new TrustManager[] { trustManager });
        }
        if (keyManager != null) {
            final String keyAlias = config.get("sslCertAlias").asString();
            httpOptions.set(OPTION_KEY_MANAGERS,
                    new KeyManager[] { keyAlias != null ? useSingleCertificate(keyAlias, keyManager) : keyManager });
        }
        return new HttpClientHandler(httpOptions);
    }
    private Duration parseCacheExpiration(final JsonValue expirationJson) {
        try {
            final Duration expiration = expirationJson.as(duration());
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -27,6 +27,10 @@
import static org.forgerock.opendj.ldap.schema.CoreSchema.getGeneralizedTimeSyntax;
import static org.forgerock.opendj.ldap.schema.CoreSchema.getIntegerSyntax;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -70,6 +74,11 @@
                }
            };
    static String readPasswordFromFile(String fileName) throws IOException {
        try (final BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)))) {
            return reader.readLine();
        }
    }
    static Object attributeToJson(final Attribute a) {
        final Function<ByteString, Object, NeverThrowsException> f = byteStringToJson(a.getAttributeDescription());
opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties
@@ -107,3 +107,5 @@
ERR_RUNTIME_EXCEPTION_61=A runtime exception occurred wile processing the request '%s': '%s'
ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID_62=The password modify request has been rejected because it is invalid. \
 A password modify request may contain two string valued fields 'oldPassword' and 'newPassword'
ERR_CONFIG_INVALID_TRUST_MANAGER_63=The trust-manager defined in '%s' is invalid: %s
ERR_CONFIG_INVALID_KEY_MANAGER_64=The key-manager defined in '%s' is invalid: %s