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-2015 ForgeRock AS.
015 */
016
017package org.forgerock.json.resource.http;
018
019import java.util.Map;
020
021import org.forgerock.services.context.Context;
022import org.forgerock.services.context.AttributesContext;
023import org.forgerock.json.resource.InternalServerErrorException;
024import org.forgerock.json.resource.ResourceException;
025import org.forgerock.services.context.SecurityContext;
026
027/**
028 * An HTTP context factory which will create a {@link SecurityContext} whose
029 * authentication ID and authorization ID are taken from attributes contained
030 * in the HTTP request.
031 * <p>
032 * This class provides integration with the common authentication framework and
033 * is intended to work as follows:
034 * <ol>
035 * <li>An incoming HTTP request is first intercepted by a HTTP filter
036 * responsible for authenticating the request.
037 * <li>If authentication is successful, the authentication filter determines the
038 * set of principals associated with the user which may be required in order to
039 * perform authorization. These principals may include the user's unique ID,
040 * realm, groups, roles, or LDAP DN, etc.
041 * <li>The authentication filter constructs a {@code Map<String, Object>}
042 * containing the principals keyed on the principal name. <b>NOTE:</b> various
043 * reserved principal names are defined in {@link SecurityContext}.
044 * <li>The authentication filter stores the authentication ID (the name which
045 * the user identified themselves with during authentication) in the HTTP
046 * servlet request's {@link #ATTRIBUTE_AUTHCID} attribute.
047 * <li>The authentication filter stores the {@code Map} containing the
048 * authorization principals in the HTTP servlet request's
049 * {@link #ATTRIBUTE_AUTHZID} attribute.
050 * <li>The JSON Resource Handler uses the {@code SecurityContextFactory} to
051 * obtain the authentication ID and authorization principals from the HTTP
052 * request's attributes.
053 * </ol>
054 * The following code illustrates how an authentication HTTP filter can
055 * populate the attributes:
056 *
057 * <pre>
058 * {@code
059 * public Promise<Response, ResponseException> filter(Context context, Request request, Handler next) {
060 *     // Authenticate the user.
061 *     String authcid = getUserName(request);
062 *     String password = getPassword(request);
063 *
064 *     // Add the attributes.
065 *     if (checkCredentials(authcid, password)) {
066 *         // Obtain principals for authorization.
067 *         Map<String, Object> authzid = new HashMap<>();
068 *         authzid.put(AUTHZID_ID, id);
069 *         ...
070 *
071 *         AttributesContext attributesContext = context.asContext(AttributesContext.class);
072 *         attributesContext.getAttributes().put(ATTRIBUTE_AUTHCID, authcid);
073 *         attributesContext.getAttributes().put(ATTRIBUTE_AUTHZID, authzid);
074 *     }
075 * }
076 * }
077 * </pre>
078 *
079 * @deprecated This class will be removed once CAF has been migrated fully to CHF, at which point components should
080 * create {@link SecurityContext}s directly rather than via request attributes.
081 */
082@Deprecated
083public final class SecurityContextFactory implements HttpContextFactory {
084
085    /**
086     * The name of the HTTP Request attribute where this factory expects to
087     * find the authenticated user's authentication ID. The name of this
088     * attribute is {@code org.forgerock.authentication.principal} and it MUST
089     * contain a {@code String} if it is present.
090     *
091     * @see SecurityContext#getAuthenticationId()
092     */
093    public static final String ATTRIBUTE_AUTHCID = "org.forgerock.authentication.principal";
094
095    /**
096     * The name of the HTTP Request attribute where this factory expects to
097     * find the authenticated user's authorization ID. The name of this
098     * attribute is {@code org.forgerock.authentication.context} and it MUST
099     * contain a {@code Map<String, Object>} if it is present.
100     *
101     * @see SecurityContext#getAuthorization()
102     */
103    public static final String ATTRIBUTE_AUTHZID = "org.forgerock.authentication.context";
104
105    // Singleton instance.
106    private static final SecurityContextFactory INSTANCE = new SecurityContextFactory();
107
108    /**
109     * Returns the singleton security context factory which can be used for
110     * obtaining context information from a HTTP request.
111     *
112     * @return The singleton security context factory.
113     */
114    public static SecurityContextFactory getHttpServletContextFactory() {
115        return INSTANCE;
116    }
117
118    private SecurityContextFactory() {
119        // Prevent instantiation.
120    }
121
122    /**
123     * Creates a new {@code SecurityContext} using the attributes contained in
124     * the provided HTTP request. The authentication ID will be obtained from
125     * the {@link #ATTRIBUTE_AUTHCID} attribute, and the authorization ID will
126     * be obtained from the {@link #ATTRIBUTE_AUTHCID} attribute.
127     * <p>
128     * It is not an error if either of the attributes are not present, but a
129     * {@link ResourceException} will be thrown if they are present but have the
130     * wrong type.
131     *
132     * @param parent
133     *            The parent context.
134     * @return A security context initialized using the attributes contained in
135     *         the provided HTTP request.
136     * @throws ResourceException
137     *             If one of the attributes was present but had the wrong type.
138     */
139    public SecurityContext createContext(Context parent) throws ResourceException {
140        AttributesContext attributesContext = parent.asContext(AttributesContext.class);
141        String authcid = getAuthenticationIdAttribute(ATTRIBUTE_AUTHCID, attributesContext);
142        Map<String, Object> authzid = getAuthorizationIdAttribute(ATTRIBUTE_AUTHZID, attributesContext);
143        return new SecurityContext(parent, authcid, authzid);
144    }
145
146    private String getAuthenticationIdAttribute(String attributeName, AttributesContext context)
147            throws InternalServerErrorException {
148        try {
149            return (String) context.getAttributes().get(attributeName);
150        } catch (final ClassCastException e) {
151            throw new InternalServerErrorException(
152                    "The security context could not be created because the "
153                            + "authentication ID attribute, " + attributeName
154                            + ", contained in the HTTP request did not have "
155                            + "the correct type", e);
156        }
157    }
158
159    @SuppressWarnings("unchecked")
160    private Map<String, Object> getAuthorizationIdAttribute(String attributeName,
161            AttributesContext context) throws InternalServerErrorException {
162        try {
163            return (Map<String, Object>) context.getAttributes().get(attributeName);
164        } catch (final ClassCastException e) {
165            throw new InternalServerErrorException(
166                    "The security context could not be created because the "
167                            + "authorization ID attribute, " + attributeName
168                            + ", contained in the HTTP request did not have "
169                            + "the correct type", e);
170        }
171    }
172
173    /**
174     * Creates a new {@code SecurityContext} using the attributes contained in
175     * the provided HTTP request. The authentication ID will be obtained from
176     * the {@link #ATTRIBUTE_AUTHCID} attribute, and the authorization ID will
177     * be obtained from the {@link #ATTRIBUTE_AUTHCID} attribute.
178     * <p>
179     * It is not an error if either of the attributes are not present, but a
180     * {@link ResourceException} will be thrown if they are present but have the
181     * wrong type.
182     *
183     * @param context
184     *            The parent context.
185     * @param request
186     *            The HTTP request from which the authentication ID and
187     *            authorization ID attributes should be obtained.
188     * @return A security context initialized using the attributes contained in
189     *         the provided HTTP request.
190     * @throws ResourceException
191     *             If one of the attributes was present but had the wrong type.
192     */
193    @Override
194    public SecurityContext createContext(Context context, org.forgerock.http.protocol.Request request)
195            throws ResourceException {
196        return createContext(context);
197    }
198}