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 }