From f098b470ee98ecd478845cd0442a141e6a9b3a54 Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Wed, 22 Jun 2016 10:02:34 +0000
Subject: [PATCH] OPENDJ-3015: Oauth2 HTTP client must support HTTPS

---
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java |  105 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 101 insertions(+), 4 deletions(-)

diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
index a59c1a7..5727ba4 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAPHttpApplication.java
+++ b/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());

--
Gitblit v1.10.0