1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package edu.internet2.middleware.shibboleth.wayf;
17
18 import java.io.File;
19 import java.lang.reflect.Constructor;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Enumeration;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.StringTokenizer;
31 import java.util.TreeMap;
32
33 import org.opensaml.saml2.metadata.EntitiesDescriptor;
34 import org.opensaml.saml2.metadata.EntityDescriptor;
35 import org.opensaml.saml2.metadata.IDPSSODescriptor;
36 import org.opensaml.saml2.metadata.Organization;
37 import org.opensaml.saml2.metadata.OrganizationDisplayName;
38 import org.opensaml.saml2.metadata.OrganizationName;
39 import org.opensaml.saml2.metadata.RoleDescriptor;
40 import org.opensaml.saml2.metadata.SPSSODescriptor;
41 import org.opensaml.saml2.metadata.provider.FileBackedHTTPMetadataProvider;
42 import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider;
43 import org.opensaml.saml2.metadata.provider.MetadataFilter;
44 import org.opensaml.saml2.metadata.provider.MetadataFilterChain;
45 import org.opensaml.saml2.metadata.provider.MetadataProvider;
46 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
47 import org.opensaml.saml2.metadata.provider.ObservableMetadataProvider;
48 import org.opensaml.xml.XMLObject;
49 import org.opensaml.xml.parse.ParserPool;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.w3c.dom.Element;
53 import org.w3c.dom.NodeList;
54
55 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
56 import edu.internet2.middleware.shibboleth.wayf.plugins.Plugin;
57 import edu.internet2.middleware.shibboleth.wayf.plugins.PluginMetadataParameter;
58 import edu.internet2.middleware.shibboleth.wayf.plugins.provider.BindingFilter;
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class IdPSiteSet implements ObservableMetadataProvider.Observer {
73
74
75 private static final Logger LOG = LoggerFactory.getLogger(IdPSiteSet.class.getName());
76
77
78 private ObservableMetadataProvider metadata;
79
80
81 private Set<String> spNames = new HashSet<String>(0);
82
83
84 private Set<String> idpNames = new HashSet<String>(0);
85
86
87 private final String identifier;
88
89
90 private final String displayName;
91
92
93 private String location;
94
95
96 private final Map<Plugin, PluginMetadataParameter> plugins = new HashMap<Plugin, PluginMetadataParameter>();
97
98
99
100
101
102
103
104
105 protected IdPSiteSet(Element el, ParserPool parserPool, boolean warnOnBadBinding) throws ShibbolethConfigurationException {
106
107 String spoolSpace;
108 String delayString;
109
110 this.identifier = el.getAttribute("identifier");
111 this.displayName = el.getAttribute("displayName");
112 location = el.getAttribute("url");
113 if (null == location || location.length() == 0) {
114
115
116
117 location = el.getAttribute("url");
118 }
119 spoolSpace = el.getAttribute("backingFile");
120 delayString = el.getAttribute("timeout");
121
122
123
124
125 String ident;
126 String className;
127 ident = "<not specified>";
128 className = "<not specified>";
129 MetadataFilterChain filterChain = null;
130 filterChain = new MetadataFilterChain();
131 try {
132 NodeList itemElements = el.getElementsByTagNameNS(XMLConstants.CONFIG_NS, "Filter");
133 List <MetadataFilter> filters = new ArrayList<MetadataFilter>(1 + itemElements.getLength());
134
135
136
137
138 filters.add(new BindingFilter(warnOnBadBinding));
139
140 for (int i = 0; i < itemElements.getLength(); i++) {
141 Element element = (Element) itemElements.item(i);
142
143 ident = "<not specified>";
144 className = "<not specified>";
145
146 ident = element.getAttribute("identifier");
147
148 if (null == ident || ident.equals("")) {
149 LOG.error("Could not load filter with no identifier");
150 continue;
151 }
152
153 className = element.getAttribute("type");
154 if (null == className || className.equals("")) {
155 LOG.error("Filter " + identifier + " did not have a valid type");
156 }
157
158
159
160 Class<MetadataFilter> filterClass = (Class<MetadataFilter>) Class.forName(className);
161 Class[] classParams = {Element.class};
162 Constructor<MetadataFilter> constructor = filterClass.getConstructor(classParams);
163 Object[] constructorParams = {element};
164
165 filters.add(constructor.newInstance(constructorParams));
166 }
167 filterChain.setFilters(filters);
168 } catch (Exception e) {
169 LOG.error("Could not load filter " + ident + "()" + className + ") for " + this.identifier, e);
170 throw new ShibbolethConfigurationException("Could not load filter", e);
171 }
172
173 LOG.info("Loading Metadata for " + displayName);
174 try {
175 int delay;
176 delay = 30000;
177 if (null != delayString && !"".equals(delayString)) {
178 delay = Integer.parseInt(delayString);
179 }
180
181 URL url = new URL(location);
182 if ("file".equalsIgnoreCase(url.getProtocol())){
183 FilesystemMetadataProvider provider = new FilesystemMetadataProvider(new File(url.getFile()));
184 provider.setParserPool(parserPool);
185 if (null != filterChain) {
186 provider.setMetadataFilter(filterChain);
187 }
188 provider.initialize();
189 metadata = provider;
190 } else {
191 if (spoolSpace == null || "".equals(spoolSpace)) {
192 throw new ShibbolethConfigurationException("backingFile must be specified for " + identifier);
193 }
194
195 FileBackedHTTPMetadataProvider provider;
196
197 provider = new FileBackedHTTPMetadataProvider(location, delay, spoolSpace);
198 provider.setParserPool(parserPool);
199 if (null != filterChain) {
200 provider.setMetadataFilter(filterChain);
201 }
202 provider.initialize();
203 metadata = provider;
204 }
205 } catch (MetadataProviderException e) {
206 throw new ShibbolethConfigurationException("Could not read " + location, e);
207 } catch (NumberFormatException e) {
208 throw new ShibbolethConfigurationException("Badly formed timeout " + delayString, e);
209 } catch (MalformedURLException e) {
210 throw new ShibbolethConfigurationException("Badly formed url ", e);
211 }
212 metadata.getObservers().add(this);
213 onEvent(metadata);
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227 private static boolean isMatch(EntityDescriptor entity, String str, HandlerConfig config) {
228
229 Enumeration input = new StringTokenizer(str);
230 while (input.hasMoreElements()) {
231 String currentToken = (String) input.nextElement();
232
233 if (config.isIgnoredForMatch(currentToken)) {
234 continue;
235 }
236
237 currentToken = currentToken.toLowerCase();
238
239 if (entity.getEntityID().indexOf(currentToken) > -1) {
240 return true;
241 }
242
243 Organization org = entity.getOrganization();
244
245 if (org != null) {
246
247 List <OrganizationName> orgNames = org.getOrganizationNames();
248 for (OrganizationName name : orgNames) {
249 if (name.getName().getLocalString().toLowerCase().indexOf(currentToken) > -1) {
250 return true;
251 }
252 }
253
254 List <OrganizationDisplayName> orgDisplayNames = org.getDisplayNames();
255 for (OrganizationDisplayName name : orgDisplayNames) {
256 if (name.getName().getLocalString().toLowerCase().indexOf(currentToken) > -1) {
257 return true;
258 }
259 }
260 }
261 }
262 return false;
263 }
264
265
266
267
268
269
270
271
272
273
274 protected Map<String, IdPSite> getIdPSites(String searchString,
275 HandlerConfig config,
276 Collection<IdPSite> searchMatches)
277 {
278 XMLObject object;
279 List <EntityDescriptor> entities;
280 try {
281 object = metadata.getMetadata();
282 } catch (MetadataProviderException e) {
283 LOG.error("Metadata for " + location + "could not be read", e);
284 return null;
285 }
286
287 if (object == null) {
288 return null;
289 }
290
291
292
293
294
295 if (object instanceof EntityDescriptor) {
296 entities = new ArrayList<EntityDescriptor>(1);
297 entities.add((EntityDescriptor) object);
298 } else if (object instanceof EntitiesDescriptor) {
299
300 EntitiesDescriptor entitiesDescriptor = (EntitiesDescriptor) object;
301
302 entities = entitiesDescriptor.getEntityDescriptors();
303 } else {
304 return null;
305 }
306
307
308
309
310
311 TreeMap<String, IdPSite> result = new TreeMap <String,IdPSite>();
312
313 for (EntityDescriptor entity : entities) {
314
315 if (entity.isValid() && hasIdPRole(entity)) {
316
317 IdPSite site = new IdPSite(entity);
318 result.put(site.getName(), site);
319 if (searchMatches != null && isMatch(entity, searchString, config)) {
320
321 searchMatches.add(site);
322 }
323
324 }
325 }
326 return result;
327 }
328
329
330
331
332
333
334 protected String getIdentifier() {
335 return identifier;
336 }
337
338
339
340
341
342 protected String getDisplayName() {
343 return displayName;
344 }
345
346
347
348
349
350
351
352
353 protected boolean containsSP(String SPName) {
354
355
356
357
358
359
360 if ((SPName == null) || (SPName.length() == 0)) {
361 return true;
362 }
363
364
365
366
367
368
369 XMLObject object;
370 try {
371 object = metadata.getMetadata();
372 } catch (MetadataProviderException e) {
373 return false;
374 }
375
376
377
378
379 if (object instanceof EntitiesDescriptor ||
380 object instanceof EntityDescriptor) {
381 return spNames.contains(SPName);
382 } else {
383 return false;
384 }
385 }
386
387
388
389
390
391
392
393
394 protected boolean containsIdP(String IdPName) {
395
396 if ((IdPName == null) || (IdPName.length() == 0)) {
397 return true;
398 }
399
400
401
402
403
404
405 XMLObject object;
406 try {
407 object = metadata.getMetadata();
408 } catch (MetadataProviderException e) {
409 return false;
410 }
411 if (object instanceof EntitiesDescriptor ||
412 object instanceof EntityDescriptor) {
413 return idpNames.contains(IdPName);
414 } else {
415 return false;
416 }
417 }
418
419
420
421
422
423
424
425
426
427
428 protected void addPlugin(Plugin plugin) {
429
430 if (plugins.containsKey(plugin)) {
431 return;
432 }
433
434 PluginMetadataParameter param = plugin.refreshMetadata(metadata);
435
436 plugins.put(plugin, param);
437 }
438
439
440
441
442
443
444 protected PluginMetadataParameter paramFor(Plugin plugin) {
445 return plugins.get(plugin);
446 }
447
448
449
450
451
452 public void onEvent(MetadataProvider provider) {
453 Set<String> spNameSet = new HashSet<String>(0);
454 Set<String> idpNameSet = new HashSet<String>(0);
455
456 XMLObject obj;
457 try {
458 obj = provider.getMetadata();
459 } catch (MetadataProviderException e) {
460 LOG.error("Couldn't read metadata for " + location, e);
461 return;
462 }
463 if ((obj instanceof EntitiesDescriptor)) {
464 EntitiesDescriptor entitiesDescriptor = (EntitiesDescriptor) obj;
465
466 for (EntityDescriptor entity : entitiesDescriptor.getEntityDescriptors()) {
467 if (hasSPRole(entity)) {
468 spNameSet.add(entity.getEntityID());
469 }
470 if (hasIdPRole(entity)) {
471 idpNameSet.add(entity.getEntityID());
472 }
473 }
474 } else if (obj instanceof EntityDescriptor) {
475 EntityDescriptor entity = (EntityDescriptor) obj;
476 if (hasSPRole(entity)) {
477 spNameSet.add(entity.getEntityID());
478 }
479 if (hasIdPRole(entity)) {
480 idpNameSet.add(entity.getEntityID());
481 }
482 } else {
483 LOG.error("Metadata for " + location + " isn't <EntitiesDescriptor> or <EntityDescriptor>");
484 return;
485 }
486
487
488
489 this.spNames = spNameSet;
490 this.idpNames = idpNameSet;
491
492 for (Plugin plugin:plugins.keySet()) {
493 plugins.put(plugin, plugin.refreshMetadata(provider));
494 }
495 }
496
497
498
499
500
501
502 private static boolean hasIdPRole(EntityDescriptor entity) {
503 List<RoleDescriptor> roles = entity.getRoleDescriptors();
504
505 for (RoleDescriptor role:roles) {
506 if (role instanceof IDPSSODescriptor) {
507
508
509
510 return true;
511 }
512 }
513 return false;
514 }
515
516
517
518
519
520
521 private static boolean hasSPRole(EntityDescriptor entity) {
522 List<RoleDescriptor> roles = entity.getRoleDescriptors();
523
524 for (RoleDescriptor role:roles) {
525 if (role instanceof SPSSODescriptor) {
526
527
528
529 return true;
530 }
531 }
532 return false;
533 }
534
535
536
537
538
539
540
541 protected IdPSite getSite(String idpName) throws WayfException {
542
543 try {
544 return new IdPSite(metadata.getEntityDescriptor(idpName));
545 } catch (MetadataProviderException e) {
546 String s = "Couldn't resolve " + idpName + " in " + getDisplayName();
547 LOG.error(s, e);
548 throw new WayfException(s, e);
549 }
550 }
551
552 protected EntityDescriptor getEntity(String name) throws WayfException {
553 try {
554 return metadata.getEntityDescriptor(name);
555 } catch (MetadataProviderException e) {
556 String s = "Couldn't resolve " + name + " in " + getDisplayName();
557 LOG.error(s, e);
558 throw new WayfException(s, e);
559 }
560
561 }
562 }
563