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