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