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}