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