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 }