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}