145c20b11a977ac71baf0226698980d6fcd95ea5
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / metadata / provider / XMLMetadataProvider.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the
6  * above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
7  * materials provided with the distribution, if any, must include the following acknowledgment: "This product includes
8  * software developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2
9  * Project. Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2,
11  * nor the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please
13  * contact shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2,
14  * UCAID, or the University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name,
15  * without prior written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS
16  * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
18  * NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS
19  * WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED
20  * INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
23  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 package edu.internet2.middleware.shibboleth.metadata.provider;
28
29 import java.net.MalformedURLException;
30 import java.net.URL;
31 import java.security.NoSuchAlgorithmException;
32 import java.text.ParseException;
33 import java.text.SimpleDateFormat;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Map;
40 import java.util.TimeZone;
41
42 import org.apache.commons.codec.binary.Hex;
43 import org.apache.log4j.Logger;
44 import org.apache.xml.security.encryption.EncryptionMethod;
45 import org.apache.xml.security.exceptions.XMLSecurityException;
46 import org.apache.xml.security.keys.KeyInfo;
47 import org.opensaml.SAMLAttribute;
48 import org.opensaml.SAMLBinding;
49 import org.opensaml.SAMLBrowserProfile;
50 import org.opensaml.SAMLException;
51 import org.opensaml.XML;
52 import org.opensaml.artifact.Artifact;
53 import org.opensaml.artifact.SAMLArtifactType0001;
54 import org.opensaml.artifact.SAMLArtifactType0002;
55 import org.opensaml.artifact.Util;
56 import org.w3c.dom.Attr;
57 import org.w3c.dom.Element;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61
62 import edu.internet2.middleware.shibboleth.common.Constants;
63 import edu.internet2.middleware.shibboleth.metadata.*;
64
65 /**
66  * @author Scott Cantor
67  */
68 public class XMLMetadataProvider implements Metadata {
69
70         private static Logger log = Logger.getLogger(XMLMetadataProvider.class.getName());
71         private Map     /* <String,ArrayList<EntityDescriptor> > */ sites = new HashMap();
72     private Map /* <String,ArrayList<EntityDescriptor> > */ sources = new HashMap();
73     private XMLEntityDescriptor rootProvider = null;
74     private XMLEntitiesDescriptor rootGroup = null;
75
76         public XMLMetadataProvider(Element e) throws SAMLException {
77         if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntitiesDescriptor"))
78             rootGroup=new XMLEntitiesDescriptor(e,this, Long.MAX_VALUE, null);
79         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntityDescriptor"))
80             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
81         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"SiteGroup"))
82             rootGroup=new XMLEntitiesDescriptor(e,this, Long.MAX_VALUE, null);
83         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"OriginSite"))
84             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
85         else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"DestinationSite"))
86             rootProvider=new XMLEntityDescriptor(e,this, Long.MAX_VALUE, null);
87         else {
88             log.error("Construction requires a valid SAML metadata file");
89             throw new MetadataException("Construction requires a valid SAML metadata file");
90         }
91         }
92
93         public EntityDescriptor lookup(String id) {
94                 ArrayList list = (ArrayList)sites.get(id);
95                 if (list != null) {
96                     long now = System.currentTimeMillis();
97             for (int i=0; i<list.size(); i++) {
98                 if (now < ((XMLEntityDescriptor)list.get(i)).getValidUntil())
99                     return (EntityDescriptor)list.get(i);
100             }
101         }
102                 return null;
103         }
104
105     public EntityDescriptor lookup(Artifact artifact) {
106         ArrayList list = null;
107         
108         if (artifact instanceof SAMLArtifactType0001) {
109             list = (ArrayList)sources.get(((SAMLArtifactType0001)artifact).getSourceId());
110         }
111         else if (artifact instanceof SAMLArtifactType0002) {
112             list = (ArrayList)sources.get(((SAMLArtifactType0002)artifact).getSourceLocation().toString());
113         }
114         else {
115             log.error("unsupported artifact type (" + artifact.getTypeCode().toString() + ")");
116         }
117
118         if (list != null) {
119             long now = System.currentTimeMillis();
120             for (int i=0; i<list.size(); i++) {
121                 if (now < ((XMLEntityDescriptor)list.get(i)).getValidUntil())
122                     return (EntityDescriptor)list.get(i);
123             }
124         }
125         return null;
126     }
127
128     class XMLEndpoint implements Endpoint {
129         private Element root = null;
130         private String binding = null;
131         private String location = null;
132         private String resploc = null;
133
134         XMLEndpoint(Element e) {
135             root = e;
136             binding = XML.assign(e.getAttributeNS(null,"Binding"));
137             location = XML.assign(e.getAttributeNS(null,"Location"));
138             resploc = XML.assign(e.getAttributeNS(null,"ResponseLocation"));
139         }
140         
141         XMLEndpoint(String binding, String location) {
142             this.binding = binding;
143             this.location = location;
144         }
145
146         public String getBinding() {
147             return binding;
148         }
149
150         public String getLocation() {
151             return location;
152         }
153
154         public String getResponseLocation() {
155             return resploc;
156         }
157         
158         public Element getElement() {
159             return root;
160         }
161     }
162
163     class XMLIndexedEndpoint extends XMLEndpoint implements IndexedEndpoint {
164         private int index = 0;
165         
166         XMLIndexedEndpoint(Element e) {
167             super(e);
168             index = Integer.parseInt(e.getAttributeNS(null,"index"));
169         }
170
171         public int getIndex() {
172             return index;
173         }
174     }
175     
176     class XMLEndpointManager implements EndpointManager {
177         private ArrayList endpoints = new ArrayList();
178         Endpoint soft = null;   // Soft default (not explicit)
179         Endpoint hard = null;   // Hard default (explicit)
180
181         public Iterator getEndpoints() {
182             return endpoints.iterator();
183         }
184         
185         public Endpoint getDefaultEndpoint() {
186             if (hard != null) return hard;
187             if (soft != null) return soft;
188             if (!endpoints.isEmpty()) return (Endpoint)endpoints.get(0);
189             return null;
190         }
191         
192         public Endpoint getEndpointByIndex(int index) {
193             for (int i=0; i < endpoints.size(); i++) {
194                 if (endpoints.get(i) instanceof IndexedEndpoint && index==((IndexedEndpoint)endpoints.get(i)).getIndex())
195                     return (Endpoint)endpoints.get(i);
196             }
197             return null;
198         }
199         
200         public Endpoint getEndpointByBinding(String binding) {
201             for (int i=0; i < endpoints.size(); i++) {
202                 if (binding.equals(((Endpoint)endpoints.get(i)).getBinding()))
203                     return (Endpoint)endpoints.get(i);
204             }
205             return null;
206         }
207         
208         protected void add(Endpoint e) {
209             endpoints.add(e);
210             if (hard == null && e.getElement() != null) {
211                 String v=XML.assign(e.getElement().getAttributeNS(null,"isDefault"));
212                 if (v != null && (v.equals("1") || v.equals("true")))  // explicit default
213                     hard=e;
214                 else if (v == null && soft == null)            // implicit default
215                     soft=e;
216             }
217             else if (hard == null && soft == null) {
218                 // No default yet, so this one qualifies as an implicit.
219                 soft=e;
220             }
221         }
222     }
223     
224     class XMLKeyDescriptor implements KeyDescriptor {
225
226         private int use = KeyDescriptor.UNSPECIFIED;
227         private KeyInfo keyInfo = null;
228         private ArrayList /* <XMLEncryptionMethod> */ methods = new ArrayList();
229
230         XMLKeyDescriptor(Element e) {
231             if (XML.safeCompare(e.getAttributeNS(null,"use"),"encryption"))
232                 use = KeyDescriptor.ENCRYPTION;
233             else if (XML.safeCompare(e.getAttributeNS(null,"use"),"signing"))
234                 use = KeyDescriptor.SIGNING;
235             
236             e = XML.getFirstChildElement(e);
237             try {
238                 keyInfo = new KeyInfo(e, null);
239             }
240             catch (XMLSecurityException e1) {
241                 log.error("unable to process ds:KeyInfo element: " + e1.getMessage());
242             }
243             
244             e = XML.getNextSiblingElement(e);
245             while (e != null && XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EncryptionMethod")) {
246                 methods.add(new XMLEncryptionMethod(e));
247             }
248         }
249
250         public int getUse() {
251             return use;
252         }
253
254         public Iterator getEncryptionMethods() {
255             return methods.iterator();
256         }
257
258         public KeyInfo getKeyInfo() {
259             return keyInfo;
260         }
261     }
262
263     class XMLEncryptionMethod implements EncryptionMethod {
264
265         String alg = null;
266         String params = null;
267         int size = 0;
268         
269         public XMLEncryptionMethod(Element e) {
270             alg = XML.assign(e.getAttributeNS(null, "Algorithm"));
271             e = XML.getFirstChildElement(e);
272             while (e != null) {
273                 if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.XMLENC_NS,"KeySize")) {
274                     if (e.hasChildNodes())
275                         size = Integer.parseInt(e.getFirstChild().getNodeValue());
276                 }
277                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.XMLENC_NS,"OAEParams")) {
278                     if (e.hasChildNodes())
279                         params = XML.assign(e.getFirstChild().getNodeValue());
280                 }
281                 e = XML.getNextSiblingElement(e);
282             }
283         }
284         
285         public String getAlgorithm() {
286             return alg;
287         }
288
289         public int getKeySize() {
290             return size;
291         }
292
293         public byte[] getOAEPparams() {
294             return params.getBytes();
295         }
296
297         public Iterator getEncryptionMethodInformation() {
298             return null;
299         }
300
301         public void setKeySize(int arg0) {
302             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
303         }
304
305         public void setOAEPparams(byte[] arg0) {
306             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
307         }
308
309         public void addEncryptionMethodInformation(Element arg0) {
310             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
311         }
312
313         public void removeEncryptionMethodInformation(Element arg0) {
314             throw new UnsupportedOperationException("EncryptionMethod implementation is read-only.");
315         }
316     }
317
318     class XMLOrganization implements Organization {
319         private Element root = null;
320         private HashMap /* <String,String> */ names = new HashMap();
321         private HashMap /* <String,String> */ displays = new HashMap();
322         private HashMap /* <String,URL> */ urls = new HashMap();
323
324         public XMLOrganization(Element e) throws MetadataException {
325             root = e;
326             e=XML.getFirstChildElement(e);
327             while (e != null) {
328                 if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationName")) {
329                     if (e.hasChildNodes()) {
330                         names.put(e.getAttributeNS(XML.XML_NS,"lang"),XML.assign(e.getFirstChild().getNodeValue()));
331                     }
332                 }
333                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationDisplayName")) {
334                     if (e.hasChildNodes()) {
335                         displays.put(e.getAttributeNS(XML.XML_NS,"lang"),XML.assign(e.getFirstChild().getNodeValue()));
336                     }
337                 }
338                 else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"OrganizationURL")) {
339                     if (e.hasChildNodes()) {
340                         URL u;
341                         try {
342                             u = new URL(e.getFirstChild().getNodeValue());
343                         }
344                         catch (MalformedURLException e1) {
345                             throw new MetadataException("OrganizationURL was invalid: " + e1);
346                         }
347                         urls.put(e.getAttributeNS(XML.XML_NS,"lang"),u);
348                     }
349                 }
350                 e=XML.getNextSiblingElement(e);
351             }
352         }
353         
354         public String getName() {
355             return getName("en");
356         }
357
358         public String getName(String lang) {
359             return (String)names.get(lang);
360         }
361
362         public String getDisplayName() {
363             return getDisplayName("en");
364         }
365
366         public String getDisplayName(String lang) {
367             return (String)displays.get(lang);
368         }
369
370         public URL getURL() {
371             return getURL("en");
372         }
373
374         public URL getURL(String lang) {
375             return (URL)urls.get(lang);
376         }
377         
378     }
379     
380         class XMLContactPerson implements ContactPerson {
381             private Element root = null;
382                 private int             type;
383         private String  company = null;
384                 private String  givenName = null;
385         private String  surName = null;
386                 private ArrayList /* <String> */ emails = new ArrayList();
387         private ArrayList /* <String> */ telephones = new ArrayList();
388
389                 public XMLContactPerson(Element e) throws MetadataException {
390             root = e;
391             String rawType = null;
392             
393             // Old metadata or new?
394             if (XML.isElementNamed(root, edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Contact")) {
395                 rawType = root.getAttributeNS(null,"Type");
396                 surName = XML.assign(root.getAttributeNS(null,"Name"));
397                 if (XML.isEmpty(surName)) {
398                     throw new MetadataException("Contact is missing Name attribute.");
399                 }
400                 if (root.hasAttributeNS(null,"Email"))
401                     emails.add(e.getAttributeNS(null,"Email"));
402             }
403             else {
404                 rawType = root.getAttributeNS(null,"contactType");
405                 Node n=null;
406                 e=XML.getFirstChildElement(root);
407                 while (e != null) {
408                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Company")) {
409                         if (e.hasChildNodes())
410                             company=XML.assign(e.getFirstChild().getNodeValue());
411                     }
412                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"GivenName")) {
413                         if (e.hasChildNodes())
414                             givenName=XML.assign(e.getFirstChild().getNodeValue());
415                     }
416                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SurName")) {
417                         if (e.hasChildNodes())
418                             surName=XML.assign(e.getFirstChild().getNodeValue());
419                     }
420                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EmailAddress")) {
421                         if (e.hasChildNodes())
422                             emails.add(XML.assign(e.getFirstChild().getNodeValue()));
423                     }
424                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"TelephoneNumber")) {
425                         if (e.hasChildNodes())
426                             telephones.add(XML.assign(e.getFirstChild().getNodeValue()));
427                     }
428                     e=XML.getNextSiblingElement(e);
429                 }
430             }
431                         
432                         if (rawType.equalsIgnoreCase("TECHNICAL")) {
433                                 type = ContactPerson.TECHNICAL;
434                         } else if (rawType.equalsIgnoreCase("SUPPORT")) {
435                                 type = ContactPerson.SUPPORT;
436                         } else if (rawType.equalsIgnoreCase("ADMINISTRATIVE")) {
437                                 type = ContactPerson.ADMINISTRATIVE;
438                         } else if (rawType.equalsIgnoreCase("BILLING")) {
439                                 type = ContactPerson.BILLING;
440                         } else if (rawType.equalsIgnoreCase("OTHER")) {
441                                 type = ContactPerson.OTHER;
442                         } else {
443                                 throw new MetadataException("Contact has unknown contact type.");
444                         }
445                 }
446
447                 public int getType() {
448                         return type;
449                 }
450
451                 public String getGivenName() {
452                         return givenName;
453                 }
454
455         public String getSurName() {
456             return surName;
457         }
458         
459         public String getCompany() {
460             return company;
461         }
462
463         public Iterator getEmailAddresses() {
464                         return emails.iterator();
465                 }
466
467                 public Iterator getTelephoneNumbers() {
468                         return telephones.iterator();
469                 }
470         
471         public Element getElement() {
472             return root;
473         }
474         }
475     
476     class Role implements RoleDescriptor {
477         private Element root = null;
478         private XMLEntityDescriptor provider = null;
479         private URL errorURL = null;
480         private Organization org = null;
481         private ArrayList /* <ContactPerson> */ contacts = new ArrayList();
482         private long validUntil = Long.MAX_VALUE;
483         protected ArrayList /* <String> */ protocolEnum = new ArrayList();
484         protected ArrayList /* <KeyDescriptor> */ keys = new ArrayList();
485
486         public Role(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
487             root = e;
488             this.validUntil = validUntil;
489             this.provider = provider;
490             
491             // Check the root element namespace. If SAML2, assume it's the std schema.
492             if (e != null && edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
493                
494                 if (e.hasAttributeNS(null,"validUntil")) {
495                     SimpleDateFormat formatter = null;
496                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
497                     int dot = dateTime.indexOf('.');
498                     if (dot > 0)
499                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
500                     else
501                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
502                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
503                     try {
504                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
505                     }
506                     catch (ParseException e1) {
507                         log.warn("Role descriptor contains invalid expiration time");
508                     }
509                 }
510                 
511                 if (e.hasAttributeNS(null,"errorURL")) {
512                     try {
513                         errorURL=new URL(e.getAttributeNS(null,"errorURL"));
514                     }
515                     catch (MalformedURLException e1) {
516                         log.error("Role descriptor contains malformed errorURL");
517                     }
518                 }
519                 
520                 // Chop the protocol list into pieces...assume any whitespace can appear in between.   
521                 protocolEnum.addAll(Arrays.asList(e.getAttributeNS(null,"protocolSupportEnumeration").split("\\s")));
522                 
523                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
524                 while (e != null) {
525                     keys.add(new XMLKeyDescriptor(e));
526                     e = XML.getNextSiblingElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
527                 }
528
529                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Organization");
530                 if (e != null)
531                     org=new XMLOrganization(e);
532
533                 e = XML.getFirstChildElement(root,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson");
534                 while (e != null) {
535                     contacts.add(new XMLContactPerson(e));
536                     e = XML.getNextSiblingElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson");
537                 }
538             }
539         }
540         
541         public EntityDescriptor getEntityDescriptor() {
542             return provider;
543         }
544
545         public Iterator getProtocolSupportEnumeration() {
546             return protocolEnum.iterator();
547         }
548
549         public boolean hasSupport(String version) {
550             return protocolEnum.contains(version);
551         }
552
553         public boolean isValid() {
554             return System.currentTimeMillis() < validUntil;
555         }
556
557         public URL getErrorURL() {
558             return (errorURL != null) ? errorURL : provider.getErrorURL();
559         }
560
561         public Iterator getKeyDescriptors() {
562             return keys.iterator();
563         }
564
565         public Organization getOrganization() {
566             return (org != null) ? org : provider.getOrganization();
567         }
568
569         public Iterator getContactPersons() {
570             return (contacts.isEmpty()) ? provider.getContactPersons() : contacts.iterator();
571         }
572
573         public Element getElement() {
574             return root;
575         }
576     }
577     
578     class SSORole extends Role implements SSODescriptor {
579         private XMLEndpointManager artifact = new XMLEndpointManager();
580         private XMLEndpointManager logout = new XMLEndpointManager();
581         private XMLEndpointManager nameid = new XMLEndpointManager();
582         private ArrayList /* <String> */ formats = new ArrayList();
583
584         public SSORole(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
585             super(provider, validUntil, e);
586             
587             // Check the root element namespace. If SAML2, assume it's the std schema.
588             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
589                 int i;
590                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ArtifactResolutionService");
591                 for (i=0; i<nlist.getLength(); i++)
592                     artifact.add(new XMLIndexedEndpoint((Element)nlist.item(i)));
593
594                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SingleLogoutService");
595                 for (i=0; i<nlist.getLength(); i++)
596                     logout.add(new XMLEndpoint((Element)nlist.item(i)));
597
598                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ManageNameIDService");
599                 for (i=0; i<nlist.getLength(); i++)
600                     nameid.add(new XMLEndpoint((Element)nlist.item(i)));
601
602                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"NameIDFormat");
603                 for (i=0; i<nlist.getLength(); i++) {
604                     if (nlist.item(i).hasChildNodes())
605                         formats.add(nlist.item(i).getNodeValue());
606                 }
607             }
608             else {
609                 // For old style, we just do SAML 1.1 compatibility with Shib handles.
610                 protocolEnum.add(XML.SAML11_PROTOCOL_ENUM);
611                 formats.add(Constants.SHIB_NAMEID_FORMAT_URI);
612             }
613         }
614
615         public EndpointManager getArtifactResolutionServiceManager() {
616             return artifact;
617         }
618
619         public EndpointManager getSingleLogoutServiceManager() {
620             return logout;
621         }
622
623         public EndpointManager getManageNameIDServiceManager() {
624             return nameid;
625         }
626
627         public Iterator getNameIDFormats() {
628             return formats.iterator();
629         }
630     }
631     
632     class IDPRole extends SSORole implements IDPSSODescriptor, ScopedRoleDescriptor {
633         private ArrayList /* <Scope> */ scopes = new ArrayList();
634         private XMLEndpointManager sso = new XMLEndpointManager();
635         private XMLEndpointManager mapping = new XMLEndpointManager();
636         private XMLEndpointManager idreq = new XMLEndpointManager();
637         private ArrayList /* <String> */ attrprofs = new ArrayList();
638         private ArrayList /* <SAMLAttribute> */ attrs = new ArrayList();
639         private boolean wantAuthnRequestsSigned = false;
640         private String sourceId = null;
641
642         public IDPRole(XMLEntityDescriptor provider, long validUntil, Element e) throws SAMLException {
643             super(provider, validUntil, e);
644             NodeList domains=null;
645             
646             // Check the root element namespace. If SAML2, assume it's the std schema.
647             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
648                 String flag=XML.assign(e.getAttributeNS(null,"WantAuthnRequestsSigned"));
649                 wantAuthnRequestsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
650                 
651                 // Check for extensions.
652                 Element ext=XML.getFirstChildElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions");
653                 if (ext != null) {
654                     Element ext1=XML.getFirstChildElement(ext,XML.SAML_ARTIFACT_SOURCEID,"SourceID");
655                     if (ext1 != null && ext1.hasChildNodes())
656                         sourceId=ext1.getFirstChild().getNodeValue();
657                     // Save off any domain elements for later.
658                     domains = ext.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"Scope");
659                 }
660                 
661                 int i;
662                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SingleSignOnService");
663                 for (i=0; i<nlist.getLength(); i++)
664                     sso.add(new XMLEndpoint((Element)(nlist.item(i))));
665
666                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"NameIDMappingService");
667                 for (i=0; i<nlist.getLength(); i++)
668                     mapping.add(new XMLEndpoint((Element)(nlist.item(i))));
669
670                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionIDRequestService");
671                 for (i=0; i<nlist.getLength(); i++)
672                     idreq.add(new XMLEndpoint((Element)(nlist.item(i))));
673
674                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeProfile");
675                 for (i=0; i<nlist.getLength(); i++) {
676                     if (nlist.item(i).hasChildNodes())
677                         attrprofs.add(nlist.item(i).getFirstChild().getNodeValue());
678                 }
679
680                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
681                 for (i=0; i<nlist.getLength(); i++) {
682                     // For now, we need to convert these to plain SAML 1.1 attributes.
683                     Element src=(Element)(nlist.item(i));
684                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
685                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
686                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
687                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
688                     while (src != null) {
689                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
690                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
691                         NamedNodeMap attrs = src.getAttributes();
692                         for (int j=0; j<attrs.getLength(); j++)
693                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
694                         while (src.hasChildNodes())
695                             val.appendChild(src.getFirstChild());
696                         copy.appendChild(val);
697                     }
698                     attrs.add(SAMLAttribute.getInstance(copy));
699                 }
700             }
701             else {
702                 attrprofs.add(Constants.SHIB_ATTRIBUTE_NAMESPACE_URI);
703                 int i;
704                 domains = e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Domain");
705                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"HandleService");
706                 for (i=0; i<nlist.getLength(); i++) {
707                     // Manufacture an endpoint for the "Shib" binding.
708                     sso.add(
709                         new XMLEndpoint(Constants.SHIB_AUTHNREQUEST_PROFILE_URI,((Element)nlist.item(i)).getAttributeNS(null,"Location"))
710                         );
711
712                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
713                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
714                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
715                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
716                     kn.appendChild(
717                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
718                         );
719                     ki.appendChild(kn);
720                     kd.appendChild(ki);
721                     kd.setAttributeNS(null,"use","signing");
722                     keys.add(new XMLKeyDescriptor(kd));
723                 }
724             }
725             
726             if (domains != null) {
727                 for (int i=0; i < domains.getLength(); i++) {
728                     String dom=(domains.item(i).hasChildNodes()) ? domains.item(i).getFirstChild().getNodeValue() : null;
729                     if (dom != null) {
730                         String regexp=XML.assign(((Element)domains.item(i)).getAttributeNS(null,"regexp"));
731                         scopes.add(
732                             new Scope(dom,(XML.safeCompare(regexp,"true") || XML.safeCompare(regexp,"1")))
733                             );
734                     }
735                 }
736             }
737         }
738
739         public Iterator getScopes() {
740             return scopes.iterator();
741         }
742
743         public boolean getWantAuthnRequestsSigned() {
744             return wantAuthnRequestsSigned;
745         }
746
747         public EndpointManager getSingleSignOnServiceManager() {
748             return sso;
749         }
750
751         public EndpointManager getNameIDMappingServiceManager() {
752             return mapping;
753         }
754
755         public EndpointManager getAssertionIDRequestServiceManager() {
756             return idreq;
757         }
758
759         public Iterator getAttributeProfiles() {
760             return attrprofs.iterator();
761         }
762
763         public Iterator getAttributes() {
764             return attrs.iterator();
765         }
766     }
767
768     class AARole extends Role implements AttributeAuthorityDescriptor, ScopedRoleDescriptor {
769         private ArrayList /* <Scope> */ scopes = new ArrayList();
770         private XMLEndpointManager query = new XMLEndpointManager();
771         private XMLEndpointManager idreq = new XMLEndpointManager();
772         private ArrayList /* <String> */ attrprofs = new ArrayList();
773         private ArrayList /* <String> */ formats = new ArrayList();
774         private ArrayList /* <SAMLAttribute> */ attrs = new ArrayList();
775
776         public AARole(XMLEntityDescriptor provider, long validUntil, Element e) throws SAMLException {
777             super(provider, validUntil, e);
778             NodeList domains=null;
779             
780             // Check the root element namespace. If SAML2, assume it's the std schema.
781             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
782                 
783                 // Check for extensions.
784                 Element ext=XML.getFirstChildElement(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Extensions");
785                 if (ext != null) {
786                     // Save off any domain elements for later.
787                     domains = ext.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIBMETA_NS,"Scope");
788                 }
789                 
790                 int i;
791                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeService");
792                 for (i=0; i<nlist.getLength(); i++)
793                     query.add(new XMLEndpoint((Element)(nlist.item(i))));
794
795                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionIDRequestService");
796                 for (i=0; i<nlist.getLength(); i++)
797                     idreq.add(new XMLEndpoint((Element)(nlist.item(i))));
798
799                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeProfile");
800                 for (i=0; i<nlist.getLength(); i++) {
801                     if (nlist.item(i).hasChildNodes())
802                         attrprofs.add(nlist.item(i).getFirstChild().getNodeValue());
803                 }
804
805                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
806                 for (i=0; i<nlist.getLength(); i++) {
807                     // For now, we need to convert these to plain SAML 1.1 attributes.
808                     Element src=(Element)(nlist.item(i));
809                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
810                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
811                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
812                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
813                     while (src != null) {
814                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
815                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
816                         NamedNodeMap attrs = src.getAttributes();
817                         for (int j=0; j<attrs.getLength(); j++)
818                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
819                         while (src.hasChildNodes())
820                             val.appendChild(src.getFirstChild());
821                         copy.appendChild(val);
822                     }
823                     attrs.add(SAMLAttribute.getInstance(copy));
824                 }
825             }
826             else {
827                 // For old style, we just do SAML 1.1 compatibility with Shib handles.
828                 protocolEnum.add(XML.SAML11_PROTOCOL_ENUM);
829                 formats.add(Constants.SHIB_NAMEID_FORMAT_URI);
830                 attrprofs.add(Constants.SHIB_ATTRIBUTE_NAMESPACE_URI);
831                 domains = e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Domain");
832                 int i;
833                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeAuthority");
834                 for (i=0; i<nlist.getLength(); i++) {
835                     // Manufacture an endpoint for the SOAP binding.
836                     query.add(
837                         new XMLEndpoint(
838                             SAMLBinding.SOAP,
839                             ((Element)nlist.item(i)).getAttributeNS(null,"Location")
840                             )
841                         );
842
843                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
844                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
845                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
846                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
847                     kn.appendChild(
848                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
849                         );
850                     ki.appendChild(kn);
851                     kd.appendChild(ki);
852                     kd.setAttributeNS(null,"use","signing");
853                     keys.add(new XMLKeyDescriptor(kd));
854                 }
855             }
856
857             if (domains != null) {
858                 for (int i=0; i < domains.getLength(); i++) {
859                     String dom=(domains.item(i).hasChildNodes()) ? domains.item(i).getFirstChild().getNodeValue() : null;
860                     if (dom != null) {
861                         String regexp=XML.assign(((Element)domains.item(i)).getAttributeNS(null,"regexp"));
862                         scopes.add(
863                             new Scope(dom,(XML.safeCompare(regexp,"true") || XML.safeCompare(regexp,"1")))
864                             );
865                     }
866                 }
867             }
868         }
869
870         public Iterator getScopes() {
871             return scopes.iterator();
872         }
873
874         public EndpointManager getAttributeServiceManager() {
875             return query;
876         }
877
878         public EndpointManager getAssertionIDRequestServiceManager() {
879             return idreq;
880         }
881
882         public Iterator getAttributeProfiles() {
883             return attrprofs.iterator();
884         }
885
886         public Iterator getAttributes() {
887             return attrs.iterator();
888         }
889
890         public Iterator getNameIDFormats() {
891             return formats.iterator();
892         }
893     }
894     
895     class SPRole extends SSORole implements SPSSODescriptor {
896         private boolean authnRequestsSigned = false;
897         private boolean wantAssertionsSigned = false;
898         private XMLEndpointManager asc = new XMLEndpointManager();
899         
900         public SPRole(XMLEntityDescriptor provider, long validUntil, Element e) throws MetadataException {
901             super(provider, validUntil, e);
902             // TODO Auto-generated constructor stub
903
904             // Check the root element namespace. If SAML2, assume it's the std schema.
905             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
906                 String flag=XML.assign(e.getAttributeNS(null,"AuthnRequestsSigned"));
907                 authnRequestsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
908                 flag=XML.assign(e.getAttributeNS(null,"WantAssertionsSigned"));
909                 wantAssertionsSigned=(XML.safeCompare(flag,"1") || XML.safeCompare(flag,"true"));
910                 
911                 int i;
912                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AssertionConsumerService");
913                 for (i=0; i<nlist.getLength(); i++)
914                     asc.add(new XMLIndexedEndpoint((Element)(nlist.item(i))));
915
916                 /*
917                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"Attribute");
918                 for (i=0; i<nlist.getLength(); i++) {
919                     // For now, we need to convert these to plain SAML 1.1 attributes.
920                     Element src=(Element)(nlist.item(i));
921                     Element copy=e.getOwnerDocument().createElementNS(XML.SAML_NS,"Attribute");
922                     copy.setAttributeNS(null,"AttributeName",src.getAttributeNS(null,"Name"));
923                     copy.setAttributeNS(null,"AttributeNamespace",src.getAttributeNS(null,"NameFormat"));
924                     src=XML.getFirstChildElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
925                     while (src != null) {
926                         src=XML.getNextSiblingElement(src,edu.internet2.middleware.shibboleth.common.XML.SAML2ASSERT_NS,"AttributeValue");
927                         Element val=e.getOwnerDocument().createElementNS(XML.SAML_NS,"AttributeValue");
928                         NamedNodeMap attrs = src.getAttributes();
929                         for (int j=0; j<attrs.getLength(); j++)
930                             val.setAttributeNodeNS((Attr)(e.getOwnerDocument().importNode(attrs.item(j),true)));
931                         while (src.hasChildNodes())
932                             val.appendChild(src.getFirstChild());
933                         copy.appendChild(val);
934                     }
935                     attrs.add(SAMLAttribute.getInstance(copy));
936                 }
937                 */
938             }
939             else {
940                 int i;
941                 NodeList nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AssertionConsumerServiceURL");
942                 for (i=0; i<nlist.getLength(); i++) {
943                     // Manufacture an endpoint for the POST profile.
944                     asc.add(
945                         new XMLEndpoint(SAMLBrowserProfile.PROFILE_POST_URI,((Element)nlist.item(i)).getAttributeNS(null,"Location"))
946                         );
947                 }
948                 
949                 nlist=e.getElementsByTagNameNS(edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeRequester");
950                 for (i=0; i<nlist.getLength(); i++) {
951                     // We're going to "mock up" a KeyDescriptor that contains the specified Name as a ds:KeyName.
952                     Element kd=e.getOwnerDocument().createElementNS(edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"KeyDescriptor");
953                     Element ki=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyInfo");
954                     Element kn=e.getOwnerDocument().createElementNS(XML.XMLSIG_NS,"KeyName");
955                     kn.appendChild(
956                         e.getOwnerDocument().createTextNode(((Element)nlist.item(i)).getAttributeNS(null,"Name"))
957                         );
958                     ki.appendChild(kn);
959                     kd.appendChild(ki);
960                     kd.setAttributeNS(null,"use","signing");
961                     keys.add(new XMLKeyDescriptor(kd));
962                 }
963             }
964         }
965
966         public boolean getAuthnRequestsSigned() {
967             return authnRequestsSigned;
968         }
969
970         public boolean getWantAssertionsSigned() {
971             return wantAssertionsSigned;
972         }
973
974         public EndpointManager getAssertionConsumerServiceManager() {
975             return asc;
976         }
977
978         public Iterator getAttributeConsumingServices() {
979             // TODO Auto-generated method stub
980             return null;
981         }
982
983         public AttributeConsumingService getDefaultAttributeConsumingService() {
984             // TODO Auto-generated method stub
985             return null;
986         }
987
988         public AttributeConsumingService getAttributeConsumingServiceByID(String id) {
989             // TODO Auto-generated method stub
990             return null;
991         }
992     }
993     
994     class XMLEntityDescriptor implements EntityDescriptor {
995         private Element root = null;
996         private EntitiesDescriptor parent = null;
997         private String id = null;
998         private URL errorURL = null;
999         private Organization org = null;
1000         private ArrayList /* <ContactPerson> */ contacts = new ArrayList();
1001         private ArrayList /* <RoleDescriptor> */ roles = new ArrayList();
1002         private AffiliationDescriptor affiliation = null;
1003         private HashMap /* <String,String> */ locs = new HashMap();
1004         private long validUntil = 0;
1005         
1006         public XMLEntityDescriptor(Element e, XMLMetadataProvider wrapper, long validUntil, EntitiesDescriptor parent) throws SAMLException {
1007             root = e;
1008             this.parent = parent;
1009             this.validUntil = validUntil;
1010
1011             // Check the root element namespace. If SAML2, assume it's the std schema.
1012             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
1013                 id=e.getAttributeNS(null,"entityID");
1014
1015                 if (e.hasAttributeNS(null,"validUntil")) {
1016                     SimpleDateFormat formatter = null;
1017                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
1018                     int dot = dateTime.indexOf('.');
1019                     if (dot > 0)
1020                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
1021                     else
1022                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
1023                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1024                     try {
1025                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
1026                     }
1027                     catch (ParseException e1) {
1028                         log.warn("Entity descriptor contains invalid expiration time");
1029                     }
1030                 }
1031
1032                 Element child=XML.getFirstChildElement(e);
1033                 while (child != null) {
1034                     // Process the various kinds of children that we care about...
1035                     if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"ContactPerson")) {
1036                         contacts.add(new XMLContactPerson(child));
1037                     }
1038                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"Organization")) {
1039                         org=new XMLOrganization(child);
1040                     }
1041                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AdditionalMetadataLocation")) {
1042                         Node loc=child.getFirstChild();
1043                         if (loc != null)
1044                             locs.put(child.getAttributeNS(null,"namespace"),loc.getNodeValue());
1045                     }
1046                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"IDPSSODescriptor")) {
1047                         roles.add(new IDPRole(this,validUntil,child));
1048                     }
1049                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"AttributeAuthorityDescriptor")) {
1050                         roles.add(new AARole(this,validUntil,child));
1051                     }
1052                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"SPSSODescriptor")) {
1053                         roles.add(new SPRole(this,validUntil,child));
1054                     }
1055                     child = XML.getNextSiblingElement(child);
1056                 }
1057             }
1058             else {
1059                 id=e.getAttributeNS(null,"Name");
1060                 if (e.hasAttributeNS(null,"ErrorURL")) {
1061                     try {
1062                         errorURL=new URL(e.getAttributeNS(null,"ErrorURL"));
1063                     }
1064                     catch (MalformedURLException e1) {
1065                         log.error("Site descriptor contains invalid ErrorURL");
1066                     }
1067                 }
1068                 
1069                 boolean idp=false,aa=false,sp=false;    // only want to build a role once
1070                 Element child=XML.getFirstChildElement(e);
1071                 while (child != null) {
1072                     // Process the various kinds of OriginSite children that we care about...
1073                     if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"Contact")) {
1074                         contacts.add(new XMLContactPerson(child));
1075                     }
1076                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"HandleService") && !idp) {
1077                         // Create the IDP role if needed.
1078                         roles.add(new IDPRole(this, validUntil, e));
1079                         idp=true;
1080                     }
1081                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeAuthority") && !aa) {
1082                         // Create the AA role if needed.
1083                         roles.add(new AARole(this, validUntil, e));
1084                         aa=true;
1085                     }
1086                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AssertionConsumerServiceURL") && !sp) {
1087                         // Create the SP role if needed.
1088                         roles.add(new SPRole(this, validUntil, e));
1089                         sp=true;
1090                     }
1091                     else if (XML.isElementNamed(child,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"AttributeRequester") && !sp) {
1092                         // Create the SP role if needed.
1093                         roles.add(new SPRole(this, validUntil, e));
1094                         sp=true;
1095                     }
1096                     child = XML.getNextSiblingElement(child);
1097                 }
1098             }
1099
1100             // Each map entry is a list of the descriptors with this ID.
1101             ArrayList list;
1102             if (wrapper.sites.containsKey(id)) {
1103                 list = (ArrayList)wrapper.sites.get(id);
1104             }
1105             else {
1106                 list = new ArrayList();
1107                 wrapper.sites.put(id,list);
1108             }
1109             list.add(this);
1110             
1111             // Look for an IdP role, and register the artifact source ID and endpoints.
1112             IDPRole idp=null;
1113             for (int i=0; i<roles.size(); i++) {
1114                 if (roles.get(i) instanceof IDPRole) {
1115                     idp = (IDPRole)roles.get(i);
1116                     if (idp.sourceId != null) {
1117                         if (wrapper.sources.containsKey(idp.sourceId)) {
1118                             list = (ArrayList)wrapper.sources.get(idp.sourceId);
1119                         }
1120                         else {
1121                             list = new ArrayList();
1122                             wrapper.sources.put(idp.sourceId,list);
1123                         }
1124                         list.add(this);
1125                     }
1126                     else {
1127                         String sourceId;
1128                         try {
1129                             sourceId = new String(Hex.encodeHex(Util.generateSourceId(id)));
1130                         }
1131                         catch (NoSuchAlgorithmException e1) {
1132                             log.error("caught exception while encoding sourceId: " + e1.getMessage());
1133                             continue;
1134                         }
1135                         if (wrapper.sources.containsKey(sourceId)) {
1136                             list = (ArrayList)wrapper.sources.get(sourceId);
1137                         }
1138                         else {
1139                             list = new ArrayList();
1140                             wrapper.sources.put(sourceId,list);
1141                         }
1142                         list.add(this);
1143                     }
1144                     Iterator locs=idp.getArtifactResolutionServiceManager().getEndpoints();
1145                     while (locs.hasNext()) {
1146                         String loc=((Endpoint)locs.next()).getLocation();
1147                         if (wrapper.sources.containsKey(loc)) {
1148                             list = (ArrayList)wrapper.sources.get(loc);
1149                         }
1150                         else {
1151                             list = new ArrayList();
1152                             wrapper.sources.put(loc,list);
1153                         }
1154                         list.add(this);
1155                     }
1156                 }
1157             }
1158         }
1159         
1160         public String getId() {
1161             return id;
1162         }
1163
1164         public boolean isValid() {
1165             return System.currentTimeMillis() < validUntil;
1166         }
1167
1168         public Iterator getRoleDescriptors() {
1169             return roles.iterator();
1170         }
1171
1172         public RoleDescriptor getRoleByType(Class type, String protocol) {
1173             for (int i=0; i<roles.size(); i++) {
1174                 RoleDescriptor role = (RoleDescriptor)roles.get(i);
1175                 if (type.isInstance(role) && role.hasSupport(protocol))
1176                     return role;
1177             }
1178             return null;
1179         }
1180
1181         public IDPSSODescriptor getIDPSSODescriptor(String protocol) {
1182             return (IDPSSODescriptor)getRoleByType(IDPSSODescriptor.class, protocol);
1183         }
1184
1185         public SPSSODescriptor getSPSSODescriptor(String protocol) {
1186             return (SPSSODescriptor)getRoleByType(SPSSODescriptor.class, protocol);
1187         }
1188
1189         public AuthnAuthorityDescriptor getAuthnAuthorityDescriptor(String protocol) {
1190             return (AuthnAuthorityDescriptor)getRoleByType(AuthnAuthorityDescriptor.class, protocol);
1191         }
1192
1193         public AttributeAuthorityDescriptor getAttributeAuthorityDescriptor(String protocol) {
1194             return (AttributeAuthorityDescriptor)getRoleByType(AttributeAuthorityDescriptor.class, protocol);
1195         }
1196
1197         public PDPDescriptor getPDPDescriptor(String protocol) {
1198             return (PDPDescriptor)getRoleByType(PDPDescriptor.class, protocol);
1199         }
1200
1201         public AffiliationDescriptor getAffiliationDescriptor() {
1202             return affiliation;
1203         }
1204
1205         public Organization getOrganization() {
1206             return org;
1207         }
1208
1209         public Iterator getContactPersons() {
1210             return contacts.iterator();
1211         }
1212
1213         public Map getAdditionalMetadataLocations() {
1214             return Collections.unmodifiableMap(locs);
1215         }
1216
1217         public EntitiesDescriptor getEntitiesDescriptor() {
1218             return parent;
1219         }
1220
1221         public Element getElement() {
1222             return root;
1223         }
1224         
1225         public long getValidUntil() {
1226             return validUntil;
1227         }
1228         
1229         public URL getErrorURL() {
1230             return errorURL;
1231         }
1232     }
1233     
1234     class XMLEntitiesDescriptor implements EntitiesDescriptor {
1235         private Element root = null;
1236         private EntitiesDescriptor parent = null;
1237         private String name = null;
1238         private ArrayList /* <EntitiesDescriptor> */ groups = new ArrayList();
1239         private ArrayList /* <EntityDescriptor> */ providers = new ArrayList();
1240         private long validUntil = Long.MAX_VALUE;
1241         
1242         public XMLEntitiesDescriptor(Element e, XMLMetadataProvider wrapper, long validUntil, EntitiesDescriptor parent) throws SAMLException {
1243             root = e;
1244             this.parent = parent;
1245             this.validUntil = validUntil;
1246             name = XML.assign(e.getAttributeNS(null, "Name"));
1247
1248             // Check the root element namespace. If SAML2, assume it's the std schema.
1249             if (edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS.equals(e.getNamespaceURI())) {
1250
1251                 if (e.hasAttributeNS(null,"validUntil")) {
1252                     SimpleDateFormat formatter = null;
1253                     String dateTime = XML.assign(e.getAttributeNS(null,"validUntil"));
1254                     int dot = dateTime.indexOf('.');
1255                     if (dot > 0)
1256                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
1257                     else
1258                         formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
1259                     formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1260                     try {
1261                         validUntil=Math.min(validUntil,formatter.parse(dateTime).getTime());
1262                     }
1263                     catch (ParseException e1) {
1264                         log.warn("Entities descriptor contains invalid expiration time");
1265                     }
1266                 }
1267
1268                 e = XML.getFirstChildElement(e);
1269                 while (e != null) {
1270                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntitiesDescriptor"))
1271                         groups.add(new XMLEntitiesDescriptor(e, wrapper, this.validUntil, this));
1272                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SAML2META_NS,"EntityDescriptor"))
1273                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1274                     e = XML.getNextSiblingElement(e);
1275                 }
1276             }
1277             else {
1278                 e = XML.getFirstChildElement(e);
1279                 while (e != null) {
1280                     if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"SiteGroup"))
1281                         groups.add(new XMLEntitiesDescriptor(e, wrapper, this.validUntil, this));
1282                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"OriginSite"))
1283                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1284                     else if (XML.isElementNamed(e,edu.internet2.middleware.shibboleth.common.XML.SHIB_NS,"DestinationSite"))
1285                         providers.add(new XMLEntityDescriptor(e, wrapper, this.validUntil, this));
1286                     e = XML.getNextSiblingElement(e);
1287                 }
1288             }
1289         }
1290
1291         public String getName() {
1292             return name;
1293         }
1294
1295         public boolean isValid() {
1296             return System.currentTimeMillis() < validUntil;
1297         }
1298
1299         public EntitiesDescriptor getEntitiesDescriptor() {
1300             return parent;
1301         }
1302
1303         public Iterator getEntitiesDescriptors() {
1304             return groups.iterator();
1305         }
1306
1307         public Iterator getEntityDescriptors() {
1308             return providers.iterator();
1309         }
1310
1311         public Element getElement() {
1312             return root;
1313         }
1314     }
1315 }