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