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