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.services.context;
018
019import static org.forgerock.util.Reject.checkNotNull;
020
021import java.util.Collections;
022import java.util.LinkedHashMap;
023import java.util.Map;
024
025import org.forgerock.json.JsonValue;
026
027/**
028 * A {@link Context} containing information about the client performing the
029 * request which may be used when performing authorization decisions. A security
030 * context will typically be created for each REST request and comprises of two
031 * fields:
032 * <ul>
033 * <li>an {@link #getAuthenticationId authentication ID} which is the principal
034 * that the client used during authentication. This might be a user name, an
035 * email address, etc. The authentication ID may be used for logging or auditing
036 * but SHOULD NOT be used when performing authorization decisions.
037 * <li>an {@link #getAuthorization authorization ID} which is a map containing
038 * additional principals associated with the client and which MAY be used when
039 * performing authorization decisions. Examples of principals include a unique
040 * identifier for the user, roles, or an LDAP distinguished name (DN).
041 * </ul>
042 * The following code illustrates how an application may obtain the realm
043 * associated with a user:
044 *
045 * <pre>
046 * Context context = ...;
047 * String realm = (String) context.asContext(SecurityContext.class).getAuthorization(AUTHZID_REALM);
048 * </pre>
049 *
050 * <pre>
051 * {
052 *   "id"     : "56f0fb7e-3837-464d-b9ec-9d3b6af665c3",
053 *   "class"  : "org.forgerock.services.context.SecurityContext",
054 *   "parent" : {
055 *       ...
056 *   },
057 *   "authenticationId" : "bjensen@example.com",
058 *   "authorization" : {
059 *       "id"        : "1230fb7e-f83b-464d-19ef-789b6af66456",
060 *       "component" : "users",
061 *       "roles"     : [
062 *           "administrators"
063 *       ],
064 *       "dn"        : "cn=bjensen,ou=people,dc=example,dc=com"
065 *   }
066 * }
067 * </pre>
068 */
069public final class SecurityContext extends AbstractContext {
070
071    /**
072     * The authorization ID name reserved for the name of the component in which
073     * a user's resource is located, e.g. "users".
074     */
075    public static final String AUTHZID_COMPONENT = "component";
076
077    /**
078     * The authorization ID name reserved for the user's LDAP distinguished
079     * name.
080     */
081    public static final String AUTHZID_DN = "dn";
082
083    /**
084     * The authorization ID principal name reserved for a user's unique
085     * identifier.
086     */
087    public static final String AUTHZID_ID = "id";
088
089    /**
090     * The authorization ID name reserved for a user's realm.
091     */
092    public static final String AUTHZID_REALM = "realm";
093
094    /**
095     * The authorization ID name reserved for the array of roles associated with
096     * the user.
097     */
098    public static final String AUTHZID_ROLES = "roles";
099
100    // Persisted attribute names
101    private static final String ATTR_AUTHENTICATION_ID = "authenticationId";
102    private static final String ATTR_AUTHORIZATION = "authorization";
103
104    /**
105     * Creates a new security context having the provided parent and an ID
106     * automatically generated using {@code UUID.randomUUID()}.
107     *
108     * @param parent
109     *            The parent context.
110     * @param authenticationId
111     *            The authentication ID that the user provided during
112     *            authentication, which may be {@code null} or empty indicating
113     *            that the client is unauthenticated.
114     * @param authorization
115     *            The authorization information which should be used for
116     *            authorizing requests may by the user, which may be
117     *            {@code null} or empty indicating that the client is is to be
118     *            treated as an anonymous user when performing authorization
119     *            decisions. The provided map will be copied defensively and
120     *            must only contain values which can be serialized as JSON
121     *            values.
122     */
123    public SecurityContext(final Context parent,
124            final String authenticationId, final Map<String, Object> authorization) {
125        this(null, parent, authenticationId, authorization); // no id
126    }
127
128    /**
129     * Creates a new security context having the provided ID, and parent.
130     *
131     * @param id
132     *            The context ID.
133     * @param parent
134     *            The parent context.
135     * @param authenticationId
136     *            The authentication ID that the user provided during
137     *            authentication, which may be {@code null} or empty indicating
138     *            that the client is unauthenticated.
139     * @param authorization
140     *            The authorization information which should be used for
141     *            authorizing requests may by the user, which may be
142     *            {@code null} or empty indicating that the client is is to be
143     *            treated as an anonymous user when performing authorization
144     *            decisions. The provided map will be copied defensively and
145     *            must only contain values which can be serialized as JSON
146     *            values.
147     */
148    public SecurityContext(final String id, final Context parent,
149            final String authenticationId, final Map<String, Object> authorization) {
150        super(id, "security", checkNotNull(parent, "Cannot instantiate SecurityContext with null parent Context"));
151        data.put(ATTR_AUTHENTICATION_ID, authenticationId != null ? authenticationId : "");
152        data.put(ATTR_AUTHORIZATION, authorization != null
153                ? Collections.unmodifiableMap(new LinkedHashMap<>(authorization))
154                : Collections.<String, Object>emptyMap());
155    }
156
157    /**
158     * Restore from JSON representation.
159     *
160     * @param savedContext
161     *            The JSON representation from which this context's attributes
162     *            should be parsed.
163     * @param classLoader
164     *            The ClassLoader which can properly resolve the persisted class-name.
165     */
166    public SecurityContext(final JsonValue savedContext, final ClassLoader classLoader) {
167        super(savedContext, classLoader);
168    }
169
170    /**
171     * Returns the principal that the client used during authentication. This
172     * might be a user name, an email address, etc. The authentication ID may be
173     * used for logging or auditing but SHOULD NOT be used for authorization
174     * decisions.
175     *
176     * @return The principal that the client used during authentication, which
177     *         may be empty (but never {@code null}) indicating that the client
178     *         is unauthenticated.
179     */
180    public String getAuthenticationId() {
181        return data.get(ATTR_AUTHENTICATION_ID).asString();
182    }
183
184    /**
185     * Returns an unmodifiable map containing additional principals associated
186     * with the client which MAY be used when performing authorization
187     * decisions. Examples of principals include a unique identifier for the
188     * user, roles, or an LDAP distinguished name (DN). The following code
189     * illustrates how an application may obtain the realm associated with a
190     * user:
191     *
192     * <pre>
193     * Context context = ...;
194     * String realm = (String) context.asContext(SecurityContext.class).getAuthorization(AUTHZID_REALM);
195     * </pre>
196     *
197     * @return An unmodifiable map containing additional principals associated
198     *         with the client which MAY be used when performing authorization
199     *         decisions. The returned map may be empty (but never {@code null})
200     *         indicating that the client is is to be treated as an anonymous
201     *         user.
202     */
203    public Map<String, Object> getAuthorization() {
204        return data.get(ATTR_AUTHORIZATION).asMap();
205    }
206}