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 2013 ForgeRock AS. 015 */ 016package org.forgerock.opendj.rest2ldap.servlet; 017 018import static org.forgerock.json.resource.Resources.*; 019import static org.forgerock.opendj.rest2ldap.Rest2LDAP.*; 020 021import java.io.InputStream; 022import java.util.Map; 023 024import javax.servlet.ServletConfig; 025import javax.servlet.ServletException; 026 027import org.codehaus.jackson.JsonParser; 028import org.codehaus.jackson.map.ObjectMapper; 029import org.forgerock.json.fluent.JsonValue; 030import org.forgerock.json.resource.CollectionResourceProvider; 031import org.forgerock.json.resource.Connection; 032import org.forgerock.json.resource.ConnectionFactory; 033import org.forgerock.json.resource.FutureResult; 034import org.forgerock.json.resource.ResourceException; 035import org.forgerock.json.resource.ResultHandler; 036import org.forgerock.json.resource.Router; 037import org.forgerock.opendj.rest2ldap.AuthorizationPolicy; 038import org.forgerock.opendj.rest2ldap.Rest2LDAP; 039import org.forgerock.opendj.rest2ldap.Rest2LDAP.Builder; 040import org.forgerock.util.Utils; 041 042/** 043 * The connection factory provider which is used by the OpenDJ Commons REST LDAP 044 * Gateway. 045 */ 046public final class Rest2LDAPConnectionFactoryProvider { 047 private static final String INIT_PARAM_CONFIG_FILE = "config-file"; 048 private static final ObjectMapper JSON_MAPPER = new ObjectMapper().configure( 049 JsonParser.Feature.ALLOW_COMMENTS, true); 050 051 /** 052 * Returns a JSON resource connection factory configured using the 053 * configuration file named in the {@code config-file} Servlet 054 * initialization parameter. See the sample configuration file for a 055 * detailed description of its content. 056 * 057 * @param config 058 * The Servlet configuration. 059 * @return The configured JSON resource connection factory. 060 * @throws ServletException 061 * If the connection factory could not be initialized. 062 * @see Rest2LDAP#configureConnectionFactory(JsonValue, String) 063 * @see Builder#configureMapping(JsonValue) 064 */ 065 public static ConnectionFactory getConnectionFactory(final ServletConfig config) 066 throws ServletException { 067 final String configFileName = config.getInitParameter(INIT_PARAM_CONFIG_FILE); 068 if (configFileName == null) { 069 throw new ServletException("Servlet initialization parameter '" 070 + INIT_PARAM_CONFIG_FILE + "' not specified"); 071 } 072 final InputStream configFile = 073 config.getServletContext().getResourceAsStream(configFileName); 074 if (configFile == null) { 075 throw new ServletException("Servlet configuration file '" + configFileName 076 + "' not found"); 077 } 078 try { 079 // Parse the config file. 080 final Object content = JSON_MAPPER.readValue(configFile, Object.class); 081 if (!(content instanceof Map)) { 082 throw new ServletException("Servlet configuration file '" + configFileName 083 + "' does not contain a valid JSON configuration"); 084 } 085 final JsonValue configuration = new JsonValue(content); 086 087 // Parse the authorization configuration. 088 final AuthorizationPolicy authzPolicy = 089 configuration.get("servlet").get("authorizationPolicy").required().asEnum( 090 AuthorizationPolicy.class); 091 final String proxyAuthzTemplate = 092 configuration.get("servlet").get("proxyAuthzIdTemplate").asString(); 093 094 // Parse the connection factory if present. 095 final String ldapFactoryName = 096 configuration.get("servlet").get("ldapConnectionFactory").asString(); 097 final org.forgerock.opendj.ldap.ConnectionFactory ldapFactory; 098 if (ldapFactoryName != null) { 099 ldapFactory = 100 configureConnectionFactory(configuration.get("ldapConnectionFactories") 101 .required(), ldapFactoryName); 102 } else { 103 ldapFactory = null; 104 } 105 106 // Create the router. 107 final Router router = new Router(); 108 final JsonValue mappings = configuration.get("servlet").get("mappings").required(); 109 for (final String mappingUrl : mappings.keys()) { 110 final JsonValue mapping = mappings.get(mappingUrl); 111 final CollectionResourceProvider provider = 112 Rest2LDAP.builder().ldapConnectionFactory(ldapFactory).authorizationPolicy( 113 authzPolicy).proxyAuthzIdTemplate(proxyAuthzTemplate) 114 .configureMapping(mapping).build(); 115 router.addRoute(mappingUrl, provider); 116 } 117 final ConnectionFactory factory = newInternalConnectionFactory(router); 118 if (ldapFactory != null) { 119 /* 120 * Return a wrapper which will release resources associated with 121 * the LDAP connection factory (pooled connections, transport, 122 * etc). 123 */ 124 return new ConnectionFactory() { 125 @Override 126 public FutureResult<Connection> getConnectionAsync( 127 ResultHandler<? super Connection> handler) { 128 return factory.getConnectionAsync(handler); 129 } 130 131 @Override 132 public Connection getConnection() throws ResourceException { 133 return factory.getConnection(); 134 } 135 136 @Override 137 public void close() { 138 ldapFactory.close(); 139 } 140 }; 141 } else { 142 return factory; 143 } 144 } catch (final ServletException e) { 145 // Rethrow. 146 throw e; 147 } catch (final Exception e) { 148 throw new ServletException("Servlet configuration file '" + configFileName 149 + "' could not be read: " + e.getMessage()); 150 } finally { 151 Utils.closeSilently(configFile); 152 } 153 } 154 155 /** Prevent instantiation. */ 156 private Rest2LDAPConnectionFactoryProvider() { 157 // Nothing to do. 158 } 159 160}