View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2012-2015 ForgeRock AS.
15   */
16  
17  package org.forgerock.json.resource.http;
18  
19  import java.util.Map;
20  
21  import org.forgerock.services.context.Context;
22  import org.forgerock.services.context.AttributesContext;
23  import org.forgerock.json.resource.InternalServerErrorException;
24  import org.forgerock.json.resource.ResourceException;
25  import org.forgerock.services.context.SecurityContext;
26  
27  /**
28   * An HTTP context factory which will create a {@link SecurityContext} whose
29   * authentication ID and authorization ID are taken from attributes contained
30   * in the HTTP request.
31   * <p>
32   * This class provides integration with the common authentication framework and
33   * is intended to work as follows:
34   * <ol>
35   * <li>An incoming HTTP request is first intercepted by a HTTP filter
36   * responsible for authenticating the request.
37   * <li>If authentication is successful, the authentication filter determines the
38   * set of principals associated with the user which may be required in order to
39   * perform authorization. These principals may include the user's unique ID,
40   * realm, groups, roles, or LDAP DN, etc.
41   * <li>The authentication filter constructs a {@code Map<String, Object>}
42   * containing the principals keyed on the principal name. <b>NOTE:</b> various
43   * reserved principal names are defined in {@link SecurityContext}.
44   * <li>The authentication filter stores the authentication ID (the name which
45   * the user identified themselves with during authentication) in the HTTP
46   * servlet request's {@link #ATTRIBUTE_AUTHCID} attribute.
47   * <li>The authentication filter stores the {@code Map} containing the
48   * authorization principals in the HTTP servlet request's
49   * {@link #ATTRIBUTE_AUTHZID} attribute.
50   * <li>The JSON Resource Handler uses the {@code SecurityContextFactory} to
51   * obtain the authentication ID and authorization principals from the HTTP
52   * request's attributes.
53   * </ol>
54   * The following code illustrates how an authentication HTTP filter can
55   * populate the attributes:
56   *
57   * <pre>
58   * {@code
59   * public Promise<Response, ResponseException> filter(Context context, Request request, Handler next) {
60   *     // Authenticate the user.
61   *     String authcid = getUserName(request);
62   *     String password = getPassword(request);
63   *
64   *     // Add the attributes.
65   *     if (checkCredentials(authcid, password)) {
66   *         // Obtain principals for authorization.
67   *         Map<String, Object> authzid = new HashMap<>();
68   *         authzid.put(AUTHZID_ID, id);
69   *         ...
70   *
71   *         AttributesContext attributesContext = context.asContext(AttributesContext.class);
72   *         attributesContext.getAttributes().put(ATTRIBUTE_AUTHCID, authcid);
73   *         attributesContext.getAttributes().put(ATTRIBUTE_AUTHZID, authzid);
74   *     }
75   * }
76   * }
77   * </pre>
78   *
79   * @deprecated This class will be removed once CAF has been migrated fully to CHF, at which point components should
80   * create {@link SecurityContext}s directly rather than via request attributes.
81   */
82  @Deprecated
83  public final class SecurityContextFactory implements HttpContextFactory {
84  
85      /**
86       * The name of the HTTP Request attribute where this factory expects to
87       * find the authenticated user's authentication ID. The name of this
88       * attribute is {@code org.forgerock.authentication.principal} and it MUST
89       * contain a {@code String} if it is present.
90       *
91       * @see SecurityContext#getAuthenticationId()
92       */
93      public static final String ATTRIBUTE_AUTHCID = "org.forgerock.authentication.principal";
94  
95      /**
96       * The name of the HTTP Request attribute where this factory expects to
97       * find the authenticated user's authorization ID. The name of this
98       * attribute is {@code org.forgerock.authentication.context} and it MUST
99       * 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 }