From a08c81f677247ec9eb7721a86250c663065e9930 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 22 Jun 2016 22:12:03 +0000
Subject: [PATCH] OPENDJ-2871 Add support for sub-resources and inheritance
---
opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LdapHttpApplication.java | 175 ++++++++++++++++++----------------------------------------
1 files changed, 55 insertions(+), 120 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 d5f56be..7953a1e 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
@@ -16,39 +16,37 @@
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.http.handler.HttpClientHandler.OPTION_KEY_MANAGERS;
+import static org.forgerock.http.handler.HttpClientHandler.OPTION_TRUST_MANAGERS;
import static org.forgerock.json.JsonValueFunctions.duration;
import static org.forgerock.json.JsonValueFunctions.enumConstant;
import static org.forgerock.json.JsonValueFunctions.setOf;
-import static org.forgerock.opendj.rest2ldap.Rest2Ldap.configureConnectionFactory;
-import static org.forgerock.opendj.rest2ldap.Utils.newLocalizedIllegalArgumentException;
+import static org.forgerock.json.resource.http.CrestHttp.newHttpHandler;
+import static org.forgerock.opendj.ldap.KeyManagers.useSingleCertificate;
+import static org.forgerock.opendj.rest2ldap.Rest2LdapJsonConfigurator.*;
+import static org.forgerock.opendj.rest2ldap.Rest2ldapMessages.*;
import static org.forgerock.opendj.rest2ldap.Utils.newJsonValueException;
-import static org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategies.*;
+import static org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategies.newSASLPlainStrategy;
+import static org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategies.newSearchThenBindStrategy;
+import static org.forgerock.opendj.rest2ldap.authz.AuthenticationStrategies.newSimpleBindStrategy;
import static org.forgerock.opendj.rest2ldap.authz.Authorizations.*;
-import static org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.*;
-import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.*;
+import static org.forgerock.opendj.rest2ldap.authz.ConditionalFilters.newConditionalFilter;
+import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.httpBasicExtractor;
+import static org.forgerock.opendj.rest2ldap.authz.CredentialExtractors.newCustomHeaderExtractor;
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.File;
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;
import java.util.Map;
import java.util.Set;
-
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -56,11 +54,6 @@
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;
-import org.forgerock.openig.oauth2.resolver.CachingAccessTokenResolver;
-import org.forgerock.openig.oauth2.resolver.OpenAmAccessTokenResolver;
import org.forgerock.http.Filter;
import org.forgerock.http.Handler;
import org.forgerock.http.HttpApplication;
@@ -71,20 +64,22 @@
import org.forgerock.http.io.Buffer;
import org.forgerock.http.protocol.Headers;
import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.json.JsonValue;
import org.forgerock.json.resource.RequestHandler;
-import org.forgerock.json.resource.Router;
-import org.forgerock.json.resource.http.CrestHttp;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
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.openig.oauth2.AccessTokenException;
+import org.forgerock.openig.oauth2.AccessTokenInfo;
+import org.forgerock.openig.oauth2.AccessTokenResolver;
+import org.forgerock.openig.oauth2.resolver.CachingAccessTokenResolver;
+import org.forgerock.openig.oauth2.resolver.OpenAmAccessTokenResolver;
import org.forgerock.services.context.SecurityContext;
import org.forgerock.util.Factory;
import org.forgerock.util.Function;
@@ -116,8 +111,8 @@
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
- /** URL to the JSON configuration file. */
- protected final URL configurationUrl;
+ /** The name of the JSON configuration directory in which config.json and rest2ldap/rest2ldap.json are located. */
+ protected final File configDirectory;
/** Schema used to perform DN validations. */
protected final Schema schema;
@@ -166,37 +161,47 @@
}
/**
- * Default constructor called by the HTTP Framework which will use the default configuration file location.
+ * Default constructor called by the HTTP Framework which will use the default configuration directory.
*/
public Rest2LdapHttpApplication() {
- this.configurationUrl = getClass().getResource("/opendj-rest2ldap-config.json");
+ try {
+ // The null check is required for unit test mocks because the resource does not exist.
+ final URL configUrl = getClass().getResource("/config.json");
+ this.configDirectory = configUrl != null ? new File(configUrl.toURI()).getParentFile() : null;
+ } catch (final URISyntaxException e) {
+ throw new IllegalStateException(e);
+ }
this.schema = Schema.getDefaultSchema();
}
/**
- * Creates a new Rest2LDAP HTTP application using the provided configuration URL.
+ * Creates a new Rest2LDAP HTTP application using the provided configuration directory.
*
- * @param configurationURL
- * The URL to the JSON configuration file
+ * @param configDirectory
+ * The name of the JSON configuration directory in which config.json and rest2ldap/rest2ldap.json are
+ * located.
* @param schema
- * The {@link Schema} used to perform DN validations
+ * The {@link Schema} used to perform DN validations
*/
- public Rest2LdapHttpApplication(final URL configurationURL, final Schema schema) {
- this.configurationUrl = checkNotNull(configurationURL, "configurationURL cannot be null");
+ public Rest2LdapHttpApplication(final File configDirectory, final Schema schema) {
+ this.configDirectory = checkNotNull(configDirectory, "configDirectory cannot be null");
this.schema = checkNotNull(schema, "schema cannot be null");
}
@Override
public final Handler start() throws HttpApplicationException {
try {
- final JsonValue configuration = readJson(configurationUrl);
+ logger.info(INFO_REST2LDAP_STARTING.get(configDirectory));
+
executorService = Executors.newSingleThreadScheduledExecutor();
- configureSecurity(configuration.get("security"));
- configureConnectionFactories(configuration.get("ldapConnectionFactories"));
- return Handlers.chainOf(
- CrestHttp.newHttpHandler(configureRest2Ldap(configuration)),
- new ErrorLoggerFilter(),
- buildAuthorizationFilter(configuration.get("authorization").required()));
+
+ final JsonValue config = readJson(new File(configDirectory, "config.json"));
+ configureSecurity(config.get("security"));
+ configureConnectionFactories(config.get("ldapConnectionFactories"));
+ final Filter authorizationFilter = buildAuthorizationFilter(config.get("authorization").required());
+ return Handlers.chainOf(newHttpHandler(configureRest2Ldap(configDirectory)),
+ new ErrorLoggerFilter(),
+ authorizationFilter);
} catch (final Exception e) {
final LocalizableMessage errorMsg = ERR_FAIL_PARSE_CONFIGURATION.get(e.getLocalizedMessage());
logger.error(errorMsg, e);
@@ -205,86 +210,16 @@
}
}
- private static JsonValue readJson(final URL resource) throws IOException {
- try (InputStream in = resource.openStream()) {
- return new JsonValue(readJsonLenient(in));
- }
- }
-
- private static RequestHandler configureRest2Ldap(final JsonValue configuration) {
- final JsonValue mappings = configuration.get("mappings").required();
- final Router router = new Router();
- for (final String mappingUrl : mappings.keys()) {
- final JsonValue mapping = mappings.get(mappingUrl);
- router.addRoute(Router.uriTemplate(mappingUrl), Rest2Ldap.builder().configureMapping(mapping).build());
- }
- return router;
+ private static RequestHandler configureRest2Ldap(final File configDirectory) throws IOException {
+ final File rest2LdapConfigDirectory = new File(configDirectory, "rest2ldap");
+ final Options options = configureOptions(readJson(new File(rest2LdapConfigDirectory, "rest2ldap.json")));
+ final File endpointsDirectory = new File(rest2LdapConfigDirectory, "endpoints");
+ return configureEndpoints(endpointsDirectory, options);
}
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);
- }
+ trustManager = configureTrustManager(configuration);
+ keyManager = configureKeyManager(configuration);
}
private void configureConnectionFactories(final JsonValue config) {
@@ -505,8 +440,8 @@
case SASL_PLAIN:
return buildSaslBindStrategy(config);
default:
- throw newLocalizedIllegalArgumentException(ERR_CONFIG_UNSUPPORTED_BIND_STRATEGY.get(
- strategy, BindStrategy.listValues()));
+ throw new LocalizedIllegalArgumentException(
+ ERR_CONFIG_UNSUPPORTED_BIND_STRATEGY.get(strategy, BindStrategy.listValues()));
}
}
--
Gitblit v1.10.0