View Javadoc

1   /*
2    * Copyright 2008 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 edu.internet2.middleware.shibboleth.wayf.plugins.provider;
18  
19  import java.util.Collection;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.opensaml.saml2.common.Extensions;
25  import org.opensaml.saml2.metadata.EntitiesDescriptor;
26  import org.opensaml.saml2.metadata.EntityDescriptor;
27  import org.opensaml.saml2.metadata.RoleDescriptor;
28  import org.opensaml.saml2.metadata.SPSSODescriptor;
29  import org.opensaml.saml2.metadata.provider.FilterException;
30  import org.opensaml.saml2.metadata.provider.MetadataFilter;
31  import org.opensaml.samlext.idpdisco.DiscoveryResponse;
32  import org.opensaml.xml.XMLObject;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  /**
37   * See SDSJ-48.  If we get a DS endpoint then we need to check that the binding is provided
38   * and that it is correct.
39   * 
40   * @author Rod Widdowson
41   *
42   */
43  public class BindingFilter implements MetadataFilter {
44  
45      /**
46       * Log for the warning. 
47       */
48      private static final Logger LOG = LoggerFactory.getLogger(BindingFilter.class.getName());
49      
50      /**
51       * Set if we just want to warn on failure.
52       */
53      private final boolean warnOnFailure;
54      
55      /**
56       * Only the protected constructor should be visible.
57       */
58      private BindingFilter() {
59          this.warnOnFailure = false;
60      }
61      
62      /**
63       * Initialize the filter.
64       * @param warn do we warn or do we fail if we see badness?
65       */
66      public BindingFilter(boolean warn) {
67          this.warnOnFailure = warn;
68      }
69      
70      /**
71       * Apply the filter.
72       * @see org.opensaml.saml2.metadata.provider.MetadataFilter#doFilter(org.opensaml.xml.XMLObject)
73       * @param metadata what to filter.
74       * @throws org.opensaml.saml2.metadata.provider.FilterException if it sees any missed or bad bindings.
75       */
76      public void doFilter(XMLObject metadata) throws FilterException {
77  
78          if (metadata instanceof EntitiesDescriptor) {
79              
80              checkEntities((EntitiesDescriptor) metadata);
81              
82          } else if (metadata instanceof EntityDescriptor) {
83              EntityDescriptor entity = (EntityDescriptor) metadata;
84              
85              if (!checkEntity(entity)) {
86                  if (warnOnFailure) {
87                      LOG.warn("Badly formatted binding for " + entity.getEntityID());
88                  } else {
89                      LOG.error("Badly formatted binding for top level entity " + entity.getEntityID());
90                  }
91              }
92          }
93      }
94  
95      /**
96       * If the entity has an SP characteristic, and it has a DS endpoint
97       * then check its binding.
98       * 
99       * @param entity what to check.
100      * @return true if all is OK.
101      */
102     private static boolean checkEntity(EntityDescriptor entity) {
103         List<RoleDescriptor> roles = entity.getRoleDescriptors();
104         
105         for (RoleDescriptor role:roles) {
106             
107             //
108             // Check every role
109             //
110             if (role instanceof SPSSODescriptor) {
111                 
112                 //
113                 // Grab hold of all the extensions for SPSSO descriptors
114                 //
115                 
116                 Extensions exts = role.getExtensions();
117                 if (exts != null) {
118                     //
119                     // We have some children check them form <DiscoveryResponse>
120                     //
121                     List<XMLObject> children = exts.getOrderedChildren();
122                     
123                     for (XMLObject obj : children) {
124                         if (obj instanceof DiscoveryResponse) {
125                             //
126                             // And check or the binding
127                             //
128                             DiscoveryResponse ds = (DiscoveryResponse) obj;
129                             String binding = ds.getBinding(); 
130 
131                             if (!DiscoveryResponse.IDP_DISCO_NS.equals(binding)) {
132                                 return false;
133                             }
134                         }
135                     }
136                 }
137             }
138         }
139         return true;
140     }
141     
142     /**
143      * Check an EntitiesDescriptor call checkentities for the Entities and ourselves
144      *  recursively for the EntitesDescriptors.
145      *  
146      * @param entities what to check.
147      */
148     private void checkEntities(EntitiesDescriptor entities) {
149         List<EntitiesDescriptor> childEntities = entities.getEntitiesDescriptors();
150         List<EntityDescriptor> children = entities.getEntityDescriptors();
151         Collection<EntityDescriptor> excludes = new HashSet<EntityDescriptor>();
152         
153         if (children != null) {
154             Iterator<EntityDescriptor> itr;
155             EntityDescriptor entity;
156             itr = children.iterator();
157             
158             while (itr.hasNext()) {
159                 entity = itr.next();
160                 if (!checkEntity(entity)) { 
161                     if (warnOnFailure) {
162                         LOG.warn("Badly formatted binding for " + entity.getEntityID());
163                     } else {
164                         LOG.error("Badly formatted binding for " + entity.getEntityID() + ". Entity has been removed");
165                         excludes.add(entity);
166                     }
167                 }
168             }
169             children.removeAll(excludes);
170         }
171         
172         if (childEntities != null) {
173             for (EntitiesDescriptor descriptor : childEntities) {
174                 checkEntities(descriptor);
175             }
176         }
177     }
178 }