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