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