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      /** Logger. */
50      private static Logger log = LoggerFactory.getLogger(EvaluableCredentialCriteriaRegistry.class);
51  
52      /** Storage for the registry mappings. */
53      private static Map<Class<? extends Criteria>, Class<? extends EvaluableCredentialCriteria>> registry;
54  
55      /** Flag to track whether registry is initialized. */
56      private static boolean initialized;
57  
58      /** Constructor. */
59      private EvaluableCredentialCriteriaRegistry() {
60      }
61  
62      /**
63       * Get an instance of EvaluableCredentialCriteria which can evaluate the supplied criteria's requirements against a
64       * Credential target.
65       * 
66       * @param criteria the criteria to be evaluated against a credential
67       * @return an instance of of EvaluableCredentialCriteria representing the specified criteria's requirements
68       * @throws SecurityException thrown if there is an error reflectively instantiating a new instance of
69       *             EvaluableCredentialCriteria based on class information stored in the registry
70       */
71      public static EvaluableCredentialCriteria getEvaluator(Criteria criteria) throws SecurityException {
72          Class<? extends EvaluableCredentialCriteria> clazz = lookup(criteria.getClass());
73  
74          if (clazz != null) {
75              log.debug("Registry located evaluable criteria class {} for criteria class {}", clazz.getName(), criteria
76                      .getClass().getName());
77  
78              try {
79  
80                  Constructor<? extends EvaluableCredentialCriteria> constructor = clazz
81                          .getConstructor(new Class[] { criteria.getClass() });
82  
83                  return constructor.newInstance(new Object[] { criteria });
84  
85              } catch (java.lang.SecurityException e) {
86                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
87                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
88              } catch (NoSuchMethodException e) {
89                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
90                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
91              } catch (IllegalArgumentException e) {
92                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
93                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
94              } catch (InstantiationException e) {
95                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
96                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
97              } catch (IllegalAccessException e) {
98                  log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
99                  throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
100             } catch (InvocationTargetException e) {
101                 log.error("Error instantiating new EvaluableCredentialCriteria instance", e);
102                 throw new SecurityException("Could not create new EvaluableCredentialCriteria", e);
103             }
104 
105         } else {
106             log.debug("Registry could not locate evaluable criteria for criteria class {}", criteria.getClass()
107                     .getName());
108         }
109         return null;
110     }
111 
112     /**
113      * Lookup the class subtype of EvaluableCredentialCriteria which is registered for the specified Criteria class.
114      * 
115      * @param clazz the Criteria class subtype to lookup
116      * @return the registered EvaluableCredentialCriteria class subtype
117      */
118     public static synchronized Class<? extends EvaluableCredentialCriteria> lookup(Class<? extends Criteria> clazz) {
119         return registry.get(clazz);
120     }
121 
122     /**
123      * Register a credential evaluator class for a criteria class.
124      * 
125      * @param criteriaClass class subtype of {@link Criteria}
126      * @param evaluableClass class subtype of {@link EvaluableCredentialCriteria}
127      */
128     public static synchronized void register(Class<? extends Criteria> criteriaClass,
129             Class<? extends EvaluableCredentialCriteria> evaluableClass) {
130 
131         log.debug("Registering class {} as evaluator for class {}", evaluableClass.getName(), criteriaClass.getName());
132 
133         registry.put(criteriaClass, evaluableClass);
134 
135     }
136 
137     /**
138      * Deregister a criteria-evaluator mapping.
139      * 
140      * @param criteriaClass class subtype of {@link Criteria}
141      */
142     public static synchronized void deregister(Class<? extends Criteria> criteriaClass) {
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         log.debug("Clearing evaluable criteria registry");
153 
154         registry.clear();
155     }
156 
157     /**
158      * Check whether the registry has been initialized.
159      * 
160      * @return true if registry is already initialized, false otherwise
161      */
162     public static synchronized boolean isInitialized() {
163         return initialized;
164     }
165 
166     /**
167      * Initialize the registry.
168      */
169     public static synchronized void init() {
170         if (isInitialized()) {
171             return;
172         }
173 
174         registry = new HashMap<Class<? extends Criteria>, Class<? extends EvaluableCredentialCriteria>>();
175 
176         loadDefaultMappings();
177 
178         initialized = true;
179     }
180 
181     /**
182      * Load the default set of criteria-evaluator mappings from the default mappings properties file.
183      */
184     public static synchronized void loadDefaultMappings() {
185         log.debug("Loading default evaluable credential criteria mappings");
186         InputStream inStream = EvaluableCredentialCriteriaRegistry.class.getResourceAsStream(DEFAULT_MAPPINGS_FILE);
187         if (inStream == null) {
188             log.error(String.format("Could not open resource stream from default mappings file '%s'",
189                     DEFAULT_MAPPINGS_FILE));
190             return;
191         }
192 
193         Properties defaultMappings = new Properties();
194         try {
195             defaultMappings.load(inStream);
196         } catch (IOException e) {
197             log.error("Error loading properties file from resource stream", e);
198             return;
199         }
200 
201         loadMappings(defaultMappings);
202     }
203 
204     /**
205      * Load a set of criteria-evaluator mappings from the supplied properties set.
206      * 
207      * @param mappings properies set where the key is the criteria class name, the value is the evaluator class name
208      */
209     @SuppressWarnings("unchecked")
210     public static synchronized void loadMappings(Properties mappings) {
211         for (Object key : mappings.keySet()) {
212             if (!(key instanceof String)) {
213                 log.error(String.format("Properties key was not an instance of String, was '%s', skipping...", key
214                         .getClass().getName()));
215                 continue;
216             }
217             String criteriaName = (String) key;
218             String evaluatorName = mappings.getProperty(criteriaName);
219 
220             ClassLoader classLoader = Configuration.class.getClassLoader();
221             Class criteriaClass = null;
222             try {
223                 criteriaClass = classLoader.loadClass(criteriaName);
224             } catch (ClassNotFoundException e) {
225                 log.error(
226                         String.format("Could not find criteria class name '%s', skipping registration", criteriaName),
227                         e);
228                 return;
229             }
230 
231             Class evaluableClass = null;
232             try {
233                 evaluableClass = classLoader.loadClass(evaluatorName);
234             } catch (ClassNotFoundException e) {
235                 log.error(String
236                         .format("Could not find evaluator class name '%s', skipping registration", criteriaName), e);
237                 return;
238             }
239 
240             register(criteriaClass, evaluableClass);
241         }
242 
243     }
244 
245     static {
246         init();
247     }
248 }