View Javadoc

1   /*
2    * Copyright [2006] [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.saml2.metadata.provider;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.Set;
23  import java.util.concurrent.locks.Lock;
24  import java.util.concurrent.locks.ReadWriteLock;
25  import java.util.concurrent.locks.ReentrantReadWriteLock;
26  
27  import javax.xml.namespace.QName;
28  
29  import org.joda.time.DateTime;
30  import org.opensaml.saml2.common.Extensions;
31  import org.opensaml.saml2.metadata.EntitiesDescriptor;
32  import org.opensaml.saml2.metadata.EntityDescriptor;
33  import org.opensaml.saml2.metadata.RoleDescriptor;
34  import org.opensaml.xml.Namespace;
35  import org.opensaml.xml.XMLObject;
36  import org.opensaml.xml.signature.Signature;
37  import org.opensaml.xml.util.IDIndex;
38  import org.opensaml.xml.util.LazySet;
39  import org.opensaml.xml.validation.ValidationException;
40  import org.opensaml.xml.validation.Validator;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  import org.w3c.dom.Element;
44  
45  /**
46   * A metadata provider that uses registered providers, in turn, to answer queries.
47   * 
48   * When searching for entity specific information (entity metadata, roles, etc.) the entity descriptor used is the first
49   * non-null descriptor found while iterating over the registered providers in insertion order.
50   * 
51   * This chaining provider implements observation by registering an observer with each contained provider. When the
52   * contained provider emits a change this provider will also emit a change to observers registered with it. As such,
53   * developers should be careful not to register a the same observer with both container providers and this provider.
54   * Doing so will result in an observer being notified twice for each change.
55   */
56  public class ChainingMetadataProvider extends BaseMetadataProvider implements ObservableMetadataProvider {
57  
58      /** Class logger. */
59      private final Logger log = LoggerFactory.getLogger(ChainingMetadataProvider.class);
60  
61      /** List of registered observers. */
62      private ArrayList<Observer> observers;
63  
64      /** Registered providers. */
65      private ArrayList<MetadataProvider> providers;
66  
67      /** Lock used to block reads during write and vice versa. */
68      private ReadWriteLock providerLock;
69  
70      /** Constructor. */
71      public ChainingMetadataProvider() {
72          super();
73          observers = new ArrayList<Observer>();
74          providers = new ArrayList<MetadataProvider>();
75          providerLock = new ReentrantReadWriteLock(true);
76      }
77  
78      /**
79       * Gets an immutable the list of currently registered providers.
80       * 
81       * @return list of currently registered providers
82       */
83      public List<MetadataProvider> getProviders() {
84          return Collections.unmodifiableList(providers);
85      }
86  
87      /**
88       * Replaces the current set of metadata providers with give collection.
89       * 
90       * @param newProviders the metadata providers to replace the current providers with
91       * 
92       * @throws MetadataProviderException thrown if there is a problem adding the metadata provider
93       */
94      public void setProviders(List<MetadataProvider> newProviders) throws MetadataProviderException {
95          providers.clear();
96          for (MetadataProvider provider : newProviders) {
97              addMetadataProvider(provider);
98          }
99      }
100 
101     /**
102      * Adds a metadata provider to the list of registered providers.
103      * 
104      * @param newProvider the provider to be added
105      * 
106      * @throws MetadataProviderException thrown if there is a problem adding the metadata provider
107      */
108     public void addMetadataProvider(MetadataProvider newProvider) throws MetadataProviderException {
109         if (newProvider != null) {
110             newProvider.setRequireValidMetadata(requireValidMetadata());
111 
112             if (newProvider instanceof ObservableMetadataProvider) {
113                 ((ObservableMetadataProvider) newProvider).getObservers().add(new ContainedProviderObserver());
114             }
115 
116             providers.add(newProvider);
117         }
118     }
119 
120     /**
121      * Removes a metadata provider from the list of registered providers.
122      * 
123      * @param provider provider to be removed
124      */
125     public void removeMetadataProvider(MetadataProvider provider) {
126         // TODO we should remove the ContainedProviderObserver from the member's observer list
127         providers.remove(provider);
128     }
129 
130     /** {@inheritDoc} */
131     public void setRequireValidMetadata(boolean requireValidMetadata) {
132         super.setRequireValidMetadata(requireValidMetadata);
133 
134         Lock writeLock = providerLock.writeLock();
135         writeLock.lock();
136         for (MetadataProvider provider : providers) {
137             provider.setRequireValidMetadata(requireValidMetadata);
138         }
139         writeLock.unlock();
140     }
141 
142     /** {@inheritDoc} */
143     public MetadataFilter getMetadataFilter() {
144         log.warn("Attempt to access unsupported MetadataFilter property on ChainingMetadataProvider");
145         return null;
146     }
147 
148     /** {@inheritDoc} */
149     public void setMetadataFilter(MetadataFilter newFilter) throws MetadataProviderException {
150         throw new UnsupportedOperationException("Metadata filter is not allowed on ChainingMetadataProvider");
151     }
152 
153     /**
154      * Gets the metadata from every registered provider and places each within a newly created EntitiesDescriptor.
155      * 
156      * {@inheritDoc}
157      */
158     public XMLObject getMetadata() throws MetadataProviderException {
159         return new ChainingEntitiesDescriptor();
160     }
161 
162     /** {@inheritDoc} */
163     public EntitiesDescriptor getEntitiesDescriptor(String name) throws MetadataProviderException {
164         Lock readLock = providerLock.readLock();
165         readLock.lock();
166 
167         EntitiesDescriptor descriptor = null;
168         try {
169             for (MetadataProvider provider : providers) {
170                 log.debug("Checking child metadata provider for entities descriptor with name: {}", name);
171                 descriptor = provider.getEntitiesDescriptor(name);
172                 if (descriptor != null) {
173                     break;
174                 }
175             }
176         } catch (MetadataProviderException e) {
177             throw e;
178         } finally {
179             readLock.unlock();
180         }
181 
182         return descriptor;
183     }
184 
185     /** {@inheritDoc} */
186     public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
187         Lock readLock = providerLock.readLock();
188         readLock.lock();
189 
190         EntityDescriptor descriptor = null;
191         try {
192             for (MetadataProvider provider : providers) {
193                 log.debug("Checking child metadata provider for entity descriptor with entity ID: {}", entityID);
194                 descriptor = provider.getEntityDescriptor(entityID);
195                 if (descriptor != null) {
196                     break;
197                 }
198             }
199         } catch (MetadataProviderException e) {
200             throw e;
201         } finally {
202             readLock.unlock();
203         }
204 
205         return descriptor;
206     }
207 
208     /** {@inheritDoc} */
209     public List<RoleDescriptor> getRole(String entityID, QName roleName) throws MetadataProviderException {
210         EntityDescriptor entityMetadata = getEntityDescriptor(entityID);
211         if (entityMetadata == null) {
212             return null;
213         }
214 
215         return entityMetadata.getRoleDescriptors(roleName);
216     }
217 
218     /** {@inheritDoc} */
219     public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol)
220             throws MetadataProviderException {
221         EntityDescriptor entityMetadata = getEntityDescriptor(entityID);
222         if (entityMetadata == null) {
223             return null;
224         }
225 
226         List<RoleDescriptor> roles = entityMetadata.getRoleDescriptors(roleName, supportedProtocol);
227         if (roles != null && !roles.isEmpty()) {
228             return roles.get(0);
229         }
230 
231         return null;
232     }
233 
234     /** {@inheritDoc} */
235     public List<Observer> getObservers() {
236         return observers;
237     }
238 
239     /**
240      * Convenience method for calling
241      * {@link org.opensaml.saml2.metadata.provider.ObservableMetadataProvider.Observer#onEvent(MetadataProvider)} on
242      * every registered Observer passing in this provider.
243      */
244     protected void emitChangeEvent() {
245         if (observers == null || observers.size() == 0) {
246             return;
247         }
248 
249         List<Observer> tempObserverList = new ArrayList<Observer>(observers);
250         for (Observer observer : tempObserverList) {
251             if (observer != null) {
252                 observer.onEvent(this);
253             }
254         }
255     }
256 
257     /**
258      * Observer that clears the descriptor index of this provider.
259      */
260     private class ContainedProviderObserver implements Observer {
261 
262         /** {@inheritDoc} */
263         public void onEvent(MetadataProvider provider) {
264             emitChangeEvent();
265         }
266     }
267 
268     /** Class that wraps the currently list of providers and exposes it as an EntitiesDescriptors. */
269     private class ChainingEntitiesDescriptor implements EntitiesDescriptor {
270 
271         /** Metadata from the child metadata providers. */
272         private ArrayList<XMLObject> childDescriptors;
273 
274         /** Constructor. */
275         public ChainingEntitiesDescriptor() {
276             childDescriptors = new ArrayList<XMLObject>();
277 
278             Lock readLock = providerLock.readLock();
279             readLock.lock();
280             try {
281                 for (MetadataProvider provider : providers) {
282                     childDescriptors.add(provider.getMetadata());
283                 }
284             } catch (MetadataProviderException e) {
285                 log.error("Unable to get metadata from child metadata provider", e);
286             } finally {
287                 readLock.unlock();
288             }
289         }
290 
291         /** {@inheritDoc} */
292         public List<EntitiesDescriptor> getEntitiesDescriptors() {
293             ArrayList<EntitiesDescriptor> descriptors = new ArrayList<EntitiesDescriptor>();
294             for (XMLObject descriptor : childDescriptors) {
295                 if (descriptor instanceof EntitiesDescriptor) {
296                     descriptors.add((EntitiesDescriptor) descriptor);
297                 }
298             }
299 
300             return descriptors;
301         }
302 
303         /** {@inheritDoc} */
304         public List<EntityDescriptor> getEntityDescriptors() {
305             ArrayList<EntityDescriptor> descriptors = new ArrayList<EntityDescriptor>();
306             for (XMLObject descriptor : childDescriptors) {
307                 if (descriptor instanceof EntityDescriptor) {
308                     descriptors.add((EntityDescriptor) descriptor);
309                 }
310             }
311 
312             return descriptors;
313         }
314 
315         /** {@inheritDoc} */
316         public Extensions getExtensions() {
317             return null;
318         }
319 
320         /** {@inheritDoc} */
321         public String getID() {
322             return null;
323         }
324 
325         /** {@inheritDoc} */
326         public String getName() {
327             return null;
328         }
329 
330         /** {@inheritDoc} */
331         public void setExtensions(Extensions extensions) {
332 
333         }
334 
335         /** {@inheritDoc} */
336         public void setID(String newID) {
337 
338         }
339 
340         /** {@inheritDoc} */
341         public void setName(String name) {
342 
343         }
344 
345         /** {@inheritDoc} */
346         public String getSignatureReferenceID() {
347             return null;
348         }
349 
350         /** {@inheritDoc} */
351         public Signature getSignature() {
352             return null;
353         }
354 
355         /** {@inheritDoc} */
356         public boolean isSigned() {
357             return false;
358         }
359 
360         /** {@inheritDoc} */
361         public void setSignature(Signature newSignature) {
362 
363         }
364 
365         /** {@inheritDoc} */
366         public void addNamespace(Namespace namespace) {
367 
368         }
369 
370         /** {@inheritDoc} */
371         public void detach() {
372 
373         }
374 
375         /** {@inheritDoc} */
376         public Element getDOM() {
377             return null;
378         }
379 
380         /** {@inheritDoc} */
381         public QName getElementQName() {
382             return EntitiesDescriptor.DEFAULT_ELEMENT_NAME;
383         }
384 
385         /** {@inheritDoc} */
386         public IDIndex getIDIndex() {
387             return null;
388         }
389 
390         /** {@inheritDoc} */
391         public Set<Namespace> getNamespaces() {
392             return new LazySet<Namespace>();
393         }
394 
395         /** {@inheritDoc} */
396         public String getNoNamespaceSchemaLocation() {
397             return null;
398         }
399 
400         /** {@inheritDoc} */
401         public List<XMLObject> getOrderedChildren() {
402             ArrayList<XMLObject> descriptors = new ArrayList<XMLObject>();
403             try {
404                 for (MetadataProvider provider : providers) {
405                     descriptors.add(provider.getMetadata());
406                 }
407             } catch (MetadataProviderException e) {
408                 log.error("Unable to generate list of child descriptors", e);
409             }
410 
411             return descriptors;
412         }
413 
414         /** {@inheritDoc} */
415         public XMLObject getParent() {
416             return null;
417         }
418 
419         /** {@inheritDoc} */
420         public String getSchemaLocation() {
421             return null;
422         }
423 
424         /** {@inheritDoc} */
425         public QName getSchemaType() {
426             return EntitiesDescriptor.TYPE_NAME;
427         }
428 
429         /** {@inheritDoc} */
430         public boolean hasChildren() {
431             return !getOrderedChildren().isEmpty();
432         }
433 
434         /** {@inheritDoc} */
435         public boolean hasParent() {
436             return false;
437         }
438 
439         /** {@inheritDoc} */
440         public void releaseChildrenDOM(boolean propagateRelease) {
441 
442         }
443 
444         /** {@inheritDoc} */
445         public void releaseDOM() {
446 
447         }
448 
449         /** {@inheritDoc} */
450         public void releaseParentDOM(boolean propagateRelease) {
451 
452         }
453 
454         /** {@inheritDoc} */
455         public void removeNamespace(Namespace namespace) {
456 
457         }
458 
459         /** {@inheritDoc} */
460         public XMLObject resolveID(String id) {
461             return null;
462         }
463 
464         /** {@inheritDoc} */
465         public XMLObject resolveIDFromRoot(String id) {
466             return null;
467         }
468 
469         /** {@inheritDoc} */
470         public void setDOM(Element dom) {
471 
472         }
473 
474         /** {@inheritDoc} */
475         public void setNoNamespaceSchemaLocation(String location) {
476 
477         }
478 
479         /** {@inheritDoc} */
480         public void setParent(XMLObject parent) {
481 
482         }
483 
484         /** {@inheritDoc} */
485         public void setSchemaLocation(String location) {
486 
487         }
488 
489         /** {@inheritDoc} */
490         public void deregisterValidator(Validator validator) {
491 
492         }
493 
494         /** {@inheritDoc} */
495         public List<Validator> getValidators() {
496             return new ArrayList<Validator>();
497         }
498 
499         /** {@inheritDoc} */
500         public void registerValidator(Validator validator) {
501         }
502 
503         /** {@inheritDoc} */
504         public void validate(boolean validateDescendants) throws ValidationException {
505         }
506 
507         /** {@inheritDoc} */
508         public DateTime getValidUntil() {
509             return null;
510         }
511 
512         /** {@inheritDoc} */
513         public boolean isValid() {
514             return true;
515         }
516 
517         /** {@inheritDoc} */
518         public void setValidUntil(DateTime validUntil) {
519 
520         }
521 
522         /** {@inheritDoc} */
523         public Long getCacheDuration() {
524             return null;
525         }
526 
527         /** {@inheritDoc} */
528         public void setCacheDuration(Long duration) {
529 
530         }
531 
532     }
533 }