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 2014 ForgeRock, AS.
015 */
016
017package org.forgerock.openam.entitlement;
018
019import com.sun.identity.entitlement.AndCondition;
020import com.sun.identity.entitlement.AndSubject;
021import com.sun.identity.entitlement.DenyOverride;
022import com.sun.identity.entitlement.EntitlementCombiner;
023import com.sun.identity.entitlement.EntitlementCondition;
024import com.sun.identity.entitlement.EntitlementSubject;
025import com.sun.identity.entitlement.NoSubject;
026import com.sun.identity.entitlement.NotCondition;
027import com.sun.identity.entitlement.NotSubject;
028import com.sun.identity.entitlement.OrCondition;
029import com.sun.identity.entitlement.OrSubject;
030import com.sun.identity.entitlement.ResourceAttribute;
031import com.sun.identity.entitlement.StaticAttributes;
032import com.sun.identity.entitlement.UserAttributes;
033
034import java.util.Map;
035import java.util.ServiceLoader;
036import java.util.Set;
037import java.util.concurrent.ConcurrentHashMap;
038import java.util.concurrent.ConcurrentMap;
039
040/**
041 * Provides methods for discovering and loading entitlements conditions and subject implementations. Builds upon the
042 * standard Java {@link ServiceLoader} mechanism to allow additional entitlement condition and subject implementations
043 * to be registered by client extensions (see {@link EntitlementModule}).
044 *
045 * @since 12.0.0
046 * @supported.all.api
047 */
048public final class EntitlementRegistry {
049
050    private final ConcurrentMap<String, Class<? extends EntitlementCondition>> conditions =
051            new ConcurrentHashMap<String, Class<? extends EntitlementCondition>>();
052    private final ConcurrentMap<String, Class<? extends EntitlementSubject>> subjects =
053            new ConcurrentHashMap<String, Class<? extends EntitlementSubject>>();
054    private final ConcurrentMap<String, Class<? extends ResourceAttribute>> attributes =
055            new ConcurrentHashMap<String, Class<? extends ResourceAttribute>>();
056    private final ConcurrentMap<String, Class<? extends EntitlementCombiner>> combiners =
057            new ConcurrentHashMap<String, Class<? extends EntitlementCombiner>>();
058
059    /**
060     * Lazy initialisation holder for loading and caching module instances. We use lazy loading to reduce startup
061     * overhead and also to ensure that classpaths are fully initialised before loading services.
062     */
063    private enum ServiceLoaderHolder {
064        INSTANCE;
065        final ServiceLoader<EntitlementModule> loader = ServiceLoader.load(EntitlementModule.class);
066    }
067
068    /**
069     * Loads all available {@link EntitlementModule} instances and registers them with a new entitlement registry.
070     * Each invocation of this method will attempt to load any known entitlement modules as per
071     * {@link ServiceLoader#load(Class)}. Previously loaded modules will be cached but any newly available modules
072     * will be loaded.
073     *
074     * @return an entitlement registry populated with all known entitlement modules.
075     */
076    public static EntitlementRegistry load() {
077
078        EntitlementRegistry registry = new EntitlementRegistry();
079
080        // Register standard logical condition and subject types.
081        registry.registerConditionType("AND", AndCondition.class);
082        registry.registerConditionType("OR", OrCondition.class);
083        registry.registerConditionType("NOT", NotCondition.class);
084
085        registry.registerSubjectType("AND", AndSubject.class);
086        registry.registerSubjectType("OR", OrSubject.class);
087        registry.registerSubjectType("NOT", NotSubject.class);
088
089        /* These conditions are not tested and were removed for OpenAM 12 release.
090           They might be reintroduced in a future release.
091
092        // Standard OpenAM entitlement conditions (policy conditions will be loaded later)
093        registry.registerConditionType(NumericAttributeCondition.class);
094        registry.registerConditionType(AttributeLookupCondition.class);
095        registry.registerConditionType(StringAttributeCondition.class);
096        // Standard OpenAM subjects
097        registry.registerSubjectType(AttributeSubject.class);
098        */
099
100        // Standard OpenAM subjects
101        registry.registerSubjectType("NONE", NoSubject.class);
102
103        // Standard OpenAM resource attribute types
104        registry.registerAttributeType("User", UserAttributes.class);
105        registry.registerAttributeType("Static", StaticAttributes.class);
106
107        // Standard OpenAM Decision Combiners
108        registry.registerDecisionCombiner(DenyOverride.class);
109
110        ServiceLoader<ConditionTypeRegistry> conditionTypeRegistries = ServiceLoader.load(ConditionTypeRegistry.class);
111        for (ConditionTypeRegistry conditionTypeRegistry : conditionTypeRegistries) {
112            for (Class<? extends EntitlementCondition> condition : conditionTypeRegistry.getEnvironmentConditions()) {
113                registry.registerConditionType(condition);
114            }
115
116            for (Class<? extends EntitlementSubject> condition : conditionTypeRegistry.getSubjectConditions()) {
117                registry.registerSubjectType(condition);
118            }
119        }
120
121        for (EntitlementModule provider : ServiceLoaderHolder.INSTANCE.loader) {
122            provider.registerCustomTypes(registry);
123        }
124
125        return registry;
126    }
127
128    /**
129     * Registers an entitlement condition type with the given short name (used in RESTful API calls and in the UI).
130     * Note: short names must be unique across all condition types.
131     *
132     * @param name the short name of the condition type.
133     * @param type the condition type to register.
134     * @throws NameAlreadyRegisteredException if the short name is already registered.
135     */
136    public void registerConditionType(String name, Class<? extends EntitlementCondition> type) {
137        register(name, conditions, type);
138    }
139
140    /**
141     * Registers an entitlement condition type using a short name generated from the type name. The short name is
142     * generated as the simple name of the class minus any {@code Condition} suffix. For example, a condition
143     * type {@code org.forgerock.openam.entitlement.TestCondition} would be registered with the short name {@code Test}.
144     *
145     * @param type the condition type to register.
146     * @throws NameAlreadyRegisteredException if the short name is already registered.
147     */
148    public void registerConditionType(Class<? extends EntitlementCondition> type) {
149        String name = type.getSimpleName().replace("Condition", "");
150        registerConditionType(name, type);
151    }
152
153    /**
154     * Returns the condition type associated with the given short name, or null if no such condition is registered.
155     *
156     * @param name the short name of the condition type to get.
157     * @return the associated condition type or null if no matching condition type is registered.
158     */
159    public Class<? extends EntitlementCondition> getConditionType(String name) {
160        return conditions.get(name);
161    }
162
163    /**
164     * Registers an entitlement combiner.
165     *
166     * @param type the condition type to register.
167     * @throws NameAlreadyRegisteredException if the short name is already registered.
168     */
169    public void registerDecisionCombiner(Class<? extends EntitlementCombiner> type) {
170        register(type.getSimpleName(), combiners, type);
171    }
172
173    /**
174     * Registers an entitlement combiner with a given name.
175     *
176     * @param type the combiner type to register.
177     * @throws NameAlreadyRegisteredException if the short name is already registered.
178     */
179    public void registerDecisionCombiner(String name, Class<? extends EntitlementCombiner> type) {
180        register(name, combiners, type);
181    }
182
183    /**
184     * Returns the combiner associated with the given short name.
185     *
186     * @param name the short name of the combiner type to get.
187     * @return the associated combiner type or null if no matching combiner type is registered.
188     */
189    public Class<? extends EntitlementCombiner> getCombinerType(String name) {
190        return combiners.get(name);
191    }
192
193    /**
194     * Registers an entitlement subject type with the given short name (used in RESTful API calls and in the UI).
195     * Note: short names must be unique across all subject types.
196     *
197     * @param name the short name of the subject type.
198     * @param type the subject type to register.
199     * @throws NameAlreadyRegisteredException if the short name is already registered.
200     */
201    public void registerSubjectType(String name, Class<? extends EntitlementSubject> type) {
202        register(name, subjects, type);
203    }
204
205    /**
206     * Registers an entitlement subject type using a short name generated from the type name. The short name is
207     * generated as the simple name of the class minus any {@code Subject} suffix. For example, a subject
208     * type {@code org.forgerock.openam.entitlement.TestSubject} would be registered with the short name {@code Test}.
209     *
210     * @param type the subject type to register.
211     * @throws NameAlreadyRegisteredException if the short name is already registered.
212     */
213    public void registerSubjectType(Class<? extends EntitlementSubject> type) {
214        String name = type.getSimpleName().replace("Subject", "");
215        registerSubjectType(name, type);
216    }
217
218    /**
219     * Returns the subject type associated with the given short name, or null if no such subject is registered.
220     *
221     * @param name the short name of the subject type to get.
222     * @return the associated subject type or null if no matching subject type is registered.
223     */
224    public Class<? extends EntitlementSubject> getSubjectType(String name) {
225        return subjects.get(name);
226    }
227
228    /**
229     * Registers a resource attribute type with the given short name (used in RESTful API calls and in the UI).
230     * Note: short names must be unique across all resource attribute types.
231     *
232     * @param name the short name of the attribute type.
233     * @param type the attribute type to register.
234     * @throws NameAlreadyRegisteredException if the short name is already registered.
235     */
236    public void registerAttributeType(String name, Class<? extends ResourceAttribute> type) {
237        register(name, attributes, type);
238    }
239
240    /**
241     * Registers a resource attribute type using a short name generated from the type name. The short name is
242     * generated as the simple name of the class minus any {@code Attribute} suffix. For example, an attribute
243     * type {@code org.forgerock.openam.entitlement.TestAttribute} would be registered with the short name {@code Test}.
244     *
245     * @param type the attribute type to register.
246     * @throws NameAlreadyRegisteredException if the short name is already registered.
247     */
248    public void registerAttributeType(Class<? extends ResourceAttribute> type) {
249        String name = type.getSimpleName().replace("Attribute", "");
250        registerAttributeType(name, type);
251    }
252
253    /**
254     * Returns the attribute type associated with the given short name, or null if no such attribute is registered.
255     *
256     * @param name the short name of the attribute type to get.
257     * @return the associated attribute type or null if no matching attribute type is registered.
258     */
259    public Class<? extends ResourceAttribute> getAttributeType(String name) {
260        return attributes.get(name);
261    }
262
263    /**
264     * Returns the short name that the given condition is registered under. If the condition is registered under
265     * multiple names then an arbitrary name is returned. If the condition type is not registered then null is returned.
266     *
267     * @param condition the condition to get a short name for.
268     * @return the short type name of the given condition or null if not registered.
269     */
270    public String getConditionName(EntitlementCondition condition) {
271        for (Map.Entry<String, Class<? extends EntitlementCondition>> candidate : conditions.entrySet()) {
272            if (candidate.getValue() == condition.getClass()) {
273                return candidate.getKey();
274            }
275        }
276        return null;
277    }
278
279    /**
280     * Returns the short name that the given subject is registered under. If the subject is registered under
281     * multiple names then an arbitrary name is returned. If the subject type is not registered then null is returned.
282     *
283     * @param subject the subject to get a short name for.
284     * @return the short type name of the given subject or null if not registered.
285     */
286    public String getSubjectName(EntitlementSubject subject) {
287        for (Map.Entry<String, Class<? extends EntitlementSubject>> candidate : subjects.entrySet()) {
288            if (candidate.getValue() == subject.getClass()) {
289                return candidate.getKey();
290            }
291        }
292        return null;
293    }
294
295    /**
296     * Returns the short name that the given attribute is registered under. If the attribute is registered under
297     * multiple names then an arbitrary name is returned. If the attribute type is not registered then null is returned.
298     *
299     * @param attribute the attribute to get a short name for.
300     * @return the short type name of the given attribute or null if not registered.
301     */
302    public String getAttributeName(ResourceAttribute attribute) {
303        for (Map.Entry<String, Class<? extends ResourceAttribute>> candidate : attributes.entrySet()) {
304            if (candidate.getValue() == attribute.getClass()) {
305                return candidate.getKey();
306            }
307        }
308        return null;
309    }
310
311    /**
312     * Registers the given type under the given short name in the given registry map. If the map already contains an
313     * entry for this short name and it is not identical to the given type then an exception is thrown and the map is
314     * not updated.
315     *
316     * @param shortName the short name to register the type under.
317     * @param map the map to register the type in.
318     * @param type the type to register.
319     * @param <T> the type of types :-)
320     * @throws NameAlreadyRegisteredException if a different type is already registered under this short name.
321     */
322    private <T> void register(String shortName, ConcurrentMap<String, Class<? extends T>> map,
323                              Class<? extends T> type) {
324        Class<? extends T> previous = map.putIfAbsent(shortName, type);
325        if (previous != null && previous != type) {
326            throw new NameAlreadyRegisteredException(shortName);
327        }
328    }
329
330    /**
331     * Returns all the short names of {@link EntitlementCondition}s currently registered in
332     * this {@link EntitlementRegistry}.
333     *
334     * @return A set of strings containing all the unqiue EntitlementConditions registered at point of query.
335     */
336    public Set<String> getConditionsShortNames() {
337        return conditions.keySet();
338    }
339
340    /**
341     * Returns all the short names of {@link EntitlementSubject}s currently registered in
342     * this {@link EntitlementRegistry}.
343     *
344     * @return A set of strings containing all the unqiue EntitlementSubject registered at point of query.
345     */
346    public Set<String> getSubjectsShortNames() {
347        return subjects.keySet();
348    }
349
350    /**
351     * Returns all the short names of {@link ResourceAttribute}s currently registered in
352     * this {@link EntitlementRegistry}.
353     *
354     * @return A set of strings containing all the unqiue ResourceAttribute registered at point of query.
355     */
356    public Set<String> getAttributesShortNames() {
357        return attributes.keySet();
358    }
359
360    /**
361     * Returns all the short names of {@link EntitlementCombiner}s currently registered in
362     * this {@link EntitlementRegistry}.
363     *
364     * @return A set of strings containing all the unqiue EntitlementCombiners registered at point of query.
365     */
366    public Set<String> getCombinersShortNames() {
367        return combiners.keySet();
368    }
369
370
371}




























































Copyright © 2010-2017, ForgeRock All Rights Reserved.