View Javadoc

1   /*
2    * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.security.credential.criteria;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.lang.reflect.Constructor;
22  import java.lang.reflect.InvocationTargetException;
23  import java.util.HashMap;
24  import java.util.Map;
25  import java.util.Properties;
26  
27  import org.opensaml.xml.Configuration;
28  import org.opensaml.xml.security.Criteria;
29  import org.opensaml.xml.security.SecurityException;
30  import org.opensaml.xml.security.credential.Credential;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  /**
35   * A registry which manages mappings from types of {@link Criteria} to the class type which can evaluate that criteria's
36   * data against a {@link Credential} target. That latter class will be a subtype of {@link EvaluableCredentialCriteria}.
37   * Each EvaluableCredentialCriteria implementation that is registered <strong>MUST</strong> implement a single-arg
38   * constructor which takes an instance of the Criteria to be evaluated. The evaluable instance is instantiated
39   * reflectively based on this requirement.
40   */
41  public final class EvaluableCredentialCriteriaRegistry {
42  
43      /**
44       * Properties file storing default mappings from criteria to evaluable credential criteria. Will be loaded as a
45       * resource stream relative to this class.
46       */
47      public static final String DEFAULT_MAPPINGS_FILE = "/credential-criteria-registry.properties";
48  
49      /** Storage for the registry mappings. */
50      private static Map<Class<? extends Criteria>, Class<? extends EvaluableCredentialCriteria>> registry;
51  
52      /** Flag to track whether registry is initialized. */
53      private static boolean initialized;
54  
55      /** Constructor. */
56      private EvaluableCredentialCriteriaRegistry() {
57      }
58  
59      /**
60       * Get an instance of EvaluableCredentialCriteria which can evaluate the supplied criteria's requirements against a
61       * Credential target.
62       * 
63       * @param criteria the criteria to be evaluated against a credential
64       * @return an instance of of EvaluableCredentialCriteria representing the specified criteria's requirements
65       * @throws SecurityException thrown if there is an error reflectively instantiating a new instance of
66       *             EvaluableCredentialCriteria based on class information stored in the registry
67       */
68      public static EvaluableCredentialCriteria getEvaluator(Criteria criteria) throws SecurityException {
69          Logger log = getLogger();
70          Class<? extends EvaluableCredentialCriteria> clazz = lookup(criteria.getClass());
71  
72          if (clazz != null) {
73              log.debug("Registry located evaluable criteria class {} for criteria class {}", clazz.getName(), criteria
74                      .getClass().getName());
75  
76              try {
77  
78                  Constructor<? extends EvaluableCredentialCriteria> constructor = clazz
79                          .getConstructor(new Class[] { criteria.getClass() });
80  
81                  return constructor.newInstance(new Object[] { criteria });
82  
83              } catch (java.lang.SecurityException e) {
84                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
85                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
86              } catch (NoSuchMethodException e) {
87                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
88                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
89              } catch (IllegalArgumentException e) {
90                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
91                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
92              } catch (InstantiationException e) {
93                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
94                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
95              } catch (IllegalAccessException e) {
96                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
97                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
98              } catch (InvocationTargetException e) {
99                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
100                 throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
101             }
102 
103         } else {
104             log.debug("Registry could not locate evaluable criteria for criteria class {}", criteria.getClass()
105                     .getName());
106         }
107         return null;
108     }
109 
110     /**
111      * Lookup the class subtype of EvaluableCredentialCriteria which is registered for the specified Criteria class.
112      * 
113      * @param clazz the Criteria class subtype to lookup
114      * @return the registered EvaluableCredentialCriteria class subtype
115      */
116     public static synchronized Class<? extends EvaluableCredentialCriteria> lookup(Class<? extends Criteria> clazz) {
117         return registry.get(clazz);
118     }
119 
120     /**
121      * Register a credential evaluator class for a criteria class.
122      * 
123      * @param criteriaClass class subtype of {@link Criteria}
124      * @param evaluableClass class subtype of {@link EvaluableCredentialCriteria}
125      */
126     public static synchronized void register(Class<? extends Criteria> criteriaClass,
127             Class<? extends EvaluableCredentialCriteria> evaluableClass) {
128         Logger log = getLogger();
129 
130         log.debug("Registering class {} as evaluator for class {}", evaluableClass.getName(), criteriaClass.getName());
131 
132         registry.put(criteriaClass, evaluableClass);
133 
134     }
135 
136     /**
137      * Deregister a criteria-evaluator mapping.
138      * 
139      * @param criteriaClass class subtype of {@link Criteria}
140      */
141     public static synchronized void deregister(Class<? extends Criteria> criteriaClass) {
142         Logger log = getLogger();
143 
144         log.debug("Deregistering evaluator for class {}", criteriaClass.getName());
145         registry.remove(criteriaClass);
146     }
147 
148     /**
149      * Clear all mappings from the registry.
150      */
151     public static synchronized void clearRegistry() {
152         Logger log = getLogger();
153         log.debug("Clearing evaluable criteria registry");
154 
155         registry.clear();
156     }
157 
158     /**
159      * Check whether the registry has been initialized.
160      * 
161      * @return true if registry is already initialized, false otherwise
162      */
163     public static synchronized boolean isInitialized() {
164         return initialized;
165     }
166 
167     /**
168      * Initialize the registry.
169      */
170     public static synchronized void init() {
171         if (isInitialized()) {
172             return;
173         }
174 
175         registry = new HashMap<Class<? extends Criteria>, Class<? extends EvaluableCredentialCriteria>>();
176 
177         loadDefaultMappings();
178 
179         initialized = true;
180     }
181 
182     /**
183      * Load the default set of criteria-evaluator mappings from the default mappings properties file.
184      */
185     public static synchronized void loadDefaultMappings() {
186         Logger log = getLogger();
187         log.debug("Loading default evaluable credential criteria mappings");
188         InputStream inStream = EvaluableCredentialCriteriaRegistry.class.getResourceAsStream(DEFAULT_MAPPINGS_FILE);
189         if (inStream == null) {
190             log.error(String.format("Could not open resource stream from default mappings file '%s'",
191                     DEFAULT_MAPPINGS_FILE));
192             return;
193         }
194 
195         Properties defaultMappings = new Properties();
196         try {
197             defaultMappings.load(inStream);
198         } catch (IOException e) {
199             log.error("Error loading properties file from resource stream", e);
200             return;
201         }
202 
203         loadMappings(defaultMappings);
204     }
205 
206     /**
207      * Load a set of criteria-evaluator mappings from the supplied properties set.
208      * 
209      * @param mappings properies set where the key is the criteria class name, the value is the evaluator class name
210      */
211     @SuppressWarnings("unchecked")
212     public static synchronized void loadMappings(Properties mappings) {
213         Logger log = getLogger();
214         for (Object key : mappings.keySet()) {
215             if (!(key instanceof String)) {
216                 log.error(String.format("Properties key was not an instance of String, was '%s', skipping...", key
217                         .getClass().getName()));
218                 continue;
219             }
220             String criteriaName = (String) key;
221             String evaluatorName = mappings.getProperty(criteriaName);
222 
223             ClassLoader classLoader = Configuration.class.getClassLoader();
224             Class criteriaClass = null;
225             try {
226                 criteriaClass = classLoader.loadClass(criteriaName);
227             } catch (ClassNotFoundException e) {
228                 log.error(
229                         String.format("Could not find criteria class name '%s', skipping registration", criteriaName),
230                         e);
231                 return;
232             }
233 
234             Class evaluableClass = null;
235             try {
236                 evaluableClass = classLoader.loadClass(evaluatorName);
237             } catch (ClassNotFoundException e) {
238                 log.error(String
239                         .format("Could not find evaluator class name '%s', skipping registration", criteriaName), e);
240                 return;
241             }
242 
243             register(criteriaClass, evaluableClass);
244         }
245 
246     }
247     
248     /**
249      * Get an SLF4J Logger.
250      * 
251      * @return a Logger instance
252      */
253     private static Logger getLogger() {
254         return LoggerFactory.getLogger(EvaluableCredentialCriteriaRegistry.class);
255     }
256 
257     static {
258         init();
259     }
260 }