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 2012-2014 ForgeRock AS. 015 */ 016package org.forgerock.opendj.rest2ldap.servlet; 017 018import javax.servlet.http.HttpServletRequest; 019 020import org.forgerock.json.resource.Context; 021import org.forgerock.json.resource.InternalServerErrorException; 022import org.forgerock.json.resource.ResourceException; 023import org.forgerock.json.resource.RootContext; 024import org.forgerock.json.resource.SecurityContext; 025import org.forgerock.json.resource.servlet.HttpServletContextFactory; 026import org.forgerock.json.resource.servlet.SecurityContextFactory; 027import org.forgerock.opendj.ldap.Connection; 028import org.forgerock.opendj.rest2ldap.AuthenticatedConnectionContext; 029 030/** 031 * An HTTP servlet context factory which will create a {@link Context} chain 032 * comprising of a {@link SecurityContext} and optionally an 033 * {@link AuthenticatedConnectionContext}. 034 * <p> 035 * This class provides integration between Rest2LDAP HTTP Servlets and the 036 * {@link Rest2LDAPAuthnFilter}, by providing a mechanism allowing the filter to 037 * pass a pre-authenticated LDAP connection through to the underlying Rest2LDAP 038 * implementation for use when performing subsequent LDAP operations. The 039 * following code illustrates how an authentication Servlet filter can populate 040 * the attributes: 041 * 042 * <pre> 043 * public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { 044 * // Authenticate the user. 045 * String username = getUserName(request); 046 * String password = getPassword(request); 047 * final Connection connection = getLDAPConnection(); 048 * 049 * // Publish the authenticated connection. 050 * try { 051 * connection.bind(username, password.toCharArray()); 052 * request.setAttribute(ATTRIBUTE_AUTHN_CONNECTION, connection); 053 * } catch (LdapException e) { 054 * // Fail the HTTP request. 055 * response.setStatus(...); 056 * return; 057 * } 058 * 059 * // Invoke the rest of the filter chain and then release the LDAP connection once 060 * // processing has completed. Note that this assumes that the filter chain is 061 * // processes requests synchronous. 062 * try { 063 * chain.doFilter(request, response); 064 * } finally { 065 * connection.close(); 066 * } 067 * } 068 * </pre> 069 */ 070public final class Rest2LDAPContextFactory implements HttpServletContextFactory { 071 072 /** 073 * The name of the HTTP Servlet Request attribute where this factory expects 074 * to find the authenticated user's authentication ID. The name of this 075 * attribute is {@code org.forgerock.security.authcid} and it MUST contain a 076 * {@code String} if it is present. 077 * 078 * @see AuthenticatedConnectionContext 079 */ 080 public static final String ATTRIBUTE_AUTHN_CONNECTION = 081 "org.forgerock.opendj.rest2ldap.authn-connection"; 082 083 /** Singleton instance. */ 084 private static final Rest2LDAPContextFactory INSTANCE = new Rest2LDAPContextFactory(); 085 086 /** 087 * Returns the singleton context factory which can be used for obtaining 088 * context information from a HTTP servlet request. 089 * <p> 090 * This method is named {@code getHttpServletContextFactory} so that it can 091 * easily be used for 092 * {@link org.forgerock.json.resource.servlet.HttpServlet#getHttpServletContextFactory 093 * configuring} JSON Resource Servlets. 094 * 095 * @return The singleton context factory. 096 */ 097 public static Rest2LDAPContextFactory getHttpServletContextFactory() { 098 return INSTANCE; 099 } 100 101 private Rest2LDAPContextFactory() { 102 // Prevent instantiation. 103 } 104 105 /** 106 * Creates a new {@link Context} chain comprising of the provided parent 107 * context(s), a {@link SecurityContext} obtained using a 108 * {@link SecurityContextFactory} , and optionally a 109 * {@code AuthenticatedConnectionContext}. The authenticated connection will 110 * be obtained from the {@link #ATTRIBUTE_AUTHN_CONNECTION} attribute 111 * contained in the provided HTTP servlet request. If the attribute is not 112 * present then the {@code AuthenticatedConnectionContext} will not be 113 * created. 114 * 115 * @param parent 116 * The parent context. 117 * @param request 118 * The HTTP servlet request from which the security and 119 * authenticated connection attributes should be obtained. 120 * @return A new {@link Context} chain comprising of the provided parent 121 * context(s), a {@link SecurityContext} obtained using a 122 * {@link SecurityContextFactory} , and optionally a 123 * {@code AuthenticatedConnectionContext}. 124 * @throws ResourceException 125 * If one of the attributes was present but had the wrong type. 126 */ 127 public Context createContext(final Context parent, final HttpServletRequest request) 128 throws ResourceException { 129 // First get the security context. 130 final Context securityContext = 131 SecurityContextFactory.getHttpServletContextFactory() 132 .createContext(parent, request); 133 134 // Now append the pre-authenticated connection context if required. 135 final Connection connection; 136 try { 137 connection = (Connection) request.getAttribute(ATTRIBUTE_AUTHN_CONNECTION); 138 } catch (final ClassCastException e) { 139 throw new InternalServerErrorException( 140 "The rest2ldap authenticated connection context could not be " 141 + "created because the connection attribute, " 142 + ATTRIBUTE_AUTHN_CONNECTION + ", contained in the HTTP " 143 + "servlet request did not have the correct type", e); 144 } 145 if (connection != null) { 146 return new AuthenticatedConnectionContext(securityContext, connection); 147 } else { 148 return securityContext; 149 } 150 } 151 152 /** 153 * Creates a new {@link Context} chain comprising of a {@link RootContext}, 154 * a {@link SecurityContext} obtained using a {@link SecurityContextFactory} 155 * , and optionally a {@code AuthenticatedConnectionContext}. The 156 * authenticated connection will be obtained from the 157 * {@link #ATTRIBUTE_AUTHN_CONNECTION} attribute contained in the provided 158 * HTTP servlet request. If the attribute is not present then the 159 * {@code AuthenticatedConnectionContext} will not be created. 160 * 161 * @param request 162 * The HTTP servlet request from which the security and 163 * authenticated connection attributes should be obtained. 164 * @return A new {@link Context} chain comprising of a {@link RootContext}, 165 * a {@link SecurityContext} obtained using a 166 * {@link SecurityContextFactory} , and optionally a 167 * {@code AuthenticatedConnectionContext}. 168 * @throws ResourceException 169 * If one of the attributes was present but had the wrong type. 170 */ 171 @Override 172 public Context createContext(final HttpServletRequest request) throws ResourceException { 173 return createContext(new RootContext(), request); 174 } 175 176}