001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015 ForgeRock AS. 015 */ 016 017package org.forgerock.opendj.rest2ldap; 018 019import static org.forgerock.http.util.Json.*; 020import static org.forgerock.opendj.rest2ldap.Rest2LDAP.configureConnectionFactory; 021import static org.forgerock.util.Utils.*; 022 023import java.io.Closeable; 024import java.io.IOException; 025import java.io.InputStream; 026import java.net.URL; 027 028import org.forgerock.http.Handler; 029import org.forgerock.http.HttpApplication; 030import org.forgerock.http.HttpApplicationException; 031import org.forgerock.http.handler.Handlers; 032import org.forgerock.http.io.Buffer; 033import org.forgerock.http.protocol.Request; 034import org.forgerock.http.protocol.Response; 035import org.forgerock.json.JsonValue; 036import org.forgerock.json.resource.CollectionResourceProvider; 037import org.forgerock.json.resource.RequestHandler; 038import org.forgerock.json.resource.Router; 039import org.forgerock.json.resource.http.CrestHttp; 040import org.forgerock.opendj.ldap.ConnectionFactory; 041import org.forgerock.services.context.Context; 042import org.forgerock.util.Factory; 043import org.forgerock.util.Reject; 044import org.forgerock.util.promise.NeverThrowsException; 045import org.forgerock.util.promise.Promise; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049/** Rest2ldap HTTP application. */ 050public final class Rest2LDAPHttpApplication implements HttpApplication { 051 private static final Logger LOG = LoggerFactory.getLogger(Rest2LDAPHttpApplication.class); 052 053 private static final class HttpHandler implements Handler, Closeable { 054 private final ConnectionFactory ldapConnectionFactory; 055 private final Handler delegate; 056 057 HttpHandler(final JsonValue configuration) { 058 ldapConnectionFactory = createLdapConnectionFactory(configuration); 059 try { 060 delegate = CrestHttp.newHttpHandler(createRouter(configuration, ldapConnectionFactory)); 061 } catch (final RuntimeException e) { 062 closeSilently(ldapConnectionFactory); 063 throw e; 064 } 065 } 066 067 private static RequestHandler createRouter( 068 final JsonValue configuration, final ConnectionFactory ldapConnectionFactory) { 069 final AuthorizationPolicy authzPolicy = configuration.get("servlet") 070 .get("authorizationPolicy") 071 .required() 072 .asEnum(AuthorizationPolicy.class); 073 final String proxyAuthzTemplate = configuration.get("servlet").get("proxyAuthzIdTemplate").asString(); 074 final JsonValue mappings = configuration.get("servlet").get("mappings").required(); 075 076 final Router router = new Router(); 077 for (final String mappingUrl : mappings.keys()) { 078 final JsonValue mapping = mappings.get(mappingUrl); 079 final CollectionResourceProvider provider = Rest2LDAP.builder() 080 .ldapConnectionFactory(ldapConnectionFactory) 081 .authorizationPolicy(authzPolicy) 082 .proxyAuthzIdTemplate(proxyAuthzTemplate) 083 .configureMapping(mapping) 084 .build(); 085 router.addRoute(Router.uriTemplate(mappingUrl), provider); 086 } 087 return router; 088 } 089 090 private static ConnectionFactory createLdapConnectionFactory(final JsonValue configuration) { 091 final String ldapFactoryName = configuration.get("servlet").get("ldapConnectionFactory").asString(); 092 if (ldapFactoryName != null) { 093 return configureConnectionFactory( 094 configuration.get("ldapConnectionFactories").required(), ldapFactoryName); 095 } 096 return null; 097 } 098 099 @Override 100 public void close() { 101 closeSilently(ldapConnectionFactory); 102 } 103 104 @Override 105 public Promise<Response, NeverThrowsException> handle(final Context context, final Request request) { 106 return delegate.handle(context, request); 107 } 108 } 109 110 private final URL configurationUrl; 111 private HttpHandler handler; 112 private HttpAuthenticationFilter filter; 113 114 /** 115 * Default constructor called by the HTTP Framework which will use the 116 * default configuration file location. 117 */ 118 public Rest2LDAPHttpApplication() { 119 this.configurationUrl = getClass().getResource("/opendj-rest2ldap-config.json"); 120 } 121 122 /** 123 * Creates a new Rest2LDAP HTTP application using the provided configuration URL. 124 * 125 * @param configurationURL 126 * The URL to the JSON configuration file. 127 */ 128 public Rest2LDAPHttpApplication(final URL configurationURL) { 129 Reject.ifNull(configurationURL, "The configuration URL must not be null"); 130 this.configurationUrl = configurationURL; 131 } 132 133 private static JsonValue readJson(final URL resource) throws IOException { 134 try (InputStream in = resource.openStream()) { 135 return new JsonValue(readJsonLenient(in)); 136 } 137 } 138 139 @Override 140 public Handler start() throws HttpApplicationException { 141 try { 142 final JsonValue configuration = readJson(configurationUrl); 143 handler = new HttpHandler(configuration); 144 filter = new HttpAuthenticationFilter(configuration); 145 return Handlers.chainOf(handler, filter); 146 } catch (final Exception e) { 147 // TODO i18n, once supported in opendj-rest2ldap 148 final String errorMsg = "Unable to start Rest2Ldap Http Application"; 149 LOG.error(errorMsg, e); 150 stop(); 151 throw new HttpApplicationException(errorMsg, e); 152 } 153 } 154 155 @Override 156 public Factory<Buffer> getBufferFactory() { 157 // Use container default buffer factory. 158 return null; 159 } 160 161 @Override 162 public void stop() { 163 closeSilently(handler, filter); 164 handler = null; 165 filter = null; 166 } 167}