46dae04ec1d5f35cf6a3b1a514392c3f663d476f
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / SAML2MetadataImpl.java
1 /*
2  * SAML2MetadataImpl.java
3  * 
4  * Process SAML 2 Metadata and present an EntityDescriptor
5  * interface.
6  * 
7  * --------------------
8  * Copyright 2002, 2004 
9  * University Corporation for Advanced Internet Development, Inc. 
10  * All rights reserved
11  * [Thats all we have to say to protect ourselves]
12  * Your permission to use this code is governed by "The Shibboleth License".
13  * A copy may be found at http://shibboleth.internet2.edu/license.html
14  * [Nothing in copyright law requires license text in every file.]
15   */
16 package edu.internet2.middleware.shibboleth.serviceprovider;
17
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Stack;
26
27 import org.apache.log4j.Logger;
28 import org.apache.xml.security.exceptions.XMLSecurityException;
29 import org.apache.xml.security.keys.KeyInfo;
30 import org.apache.xmlbeans.XmlException;
31 import org.opensaml.SAMLAttributeDesignator;
32 import org.w3.x2001.x04.xmlenc.EncryptionMethodType;
33 import org.w3c.dom.Element;
34 import org.w3c.dom.Node;
35
36 import x0Metadata.oasisNamesTcSAML2.AttributeAuthorityDescriptorType;
37 import x0Metadata.oasisNamesTcSAML2.ContactType;
38 import x0Metadata.oasisNamesTcSAML2.EndpointType;
39 import x0Metadata.oasisNamesTcSAML2.EntitiesDescriptorDocument;
40 import x0Metadata.oasisNamesTcSAML2.EntitiesDescriptorType;
41 import x0Metadata.oasisNamesTcSAML2.EntityDescriptorType;
42 import x0Metadata.oasisNamesTcSAML2.IDPSSODescriptorType;
43 import x0Metadata.oasisNamesTcSAML2.KeyDescriptorType;
44 import x0Metadata.oasisNamesTcSAML2.RoleDescriptorType;
45 import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityRole;
46 import edu.internet2.middleware.shibboleth.metadata.ContactPerson;
47 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
48 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
49 import edu.internet2.middleware.shibboleth.metadata.EntityLocator;
50 import edu.internet2.middleware.shibboleth.metadata.IDPProviderRole;
51 import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
52 import edu.internet2.middleware.shibboleth.metadata.Provider;
53 import edu.internet2.middleware.shibboleth.metadata.ProviderRole;
54
55
56 /**
57  * Shibboleth 1.2 XML Metadata support
58  */
59 class SAML2MetadataImpl 
60         implements 
61                 EntityLocator /* renamed "Metadata" interface */, 
62                 PluggableConfigurationComponent
63         {
64     
65     private static Logger log = Logger.getLogger(SAML2MetadataImpl.class);
66     
67         Map entityDescriptors = new HashMap();
68         
69
70         public void initialize(Node dom) 
71                 throws XmlException {
72             EntitiesDescriptorDocument bean=null;
73         bean = EntitiesDescriptorDocument.Factory.parse(dom);
74         Stack parentgroups = new Stack();
75                 processGroup(bean.getEntitiesDescriptor(),parentgroups);
76         }
77         
78         public String getSchemaPathname() {
79             return "/schemas/sstc-saml-schema-metadata-2.0.xsd";
80         }
81         
82         /**
83          * Drill down (recursively) through groups to wrap each
84          * OriginSite in a ProviderImp and index by Name.
85          * 
86          * @param group SiteGroup
87          */
88         private void processGroup(
89                 EntitiesDescriptorType group, 
90                 Stack /*<EntitiesDescriptorType>*/ parentgroups) {
91                 parentgroups.push(group);
92                 EntitiesDescriptorType[] parents = 
93                     new EntitiesDescriptorType[parentgroups.size()];
94                 Iterator/*<EntitiesDescriptorType>*/ iterator = parentgroups.iterator();
95                 for (int i=0;i<parentgroups.size();i++) {
96                     parents[i]=(EntitiesDescriptorType)iterator.next();
97                 }
98                 EntityDescriptorType[] sites = group.getEntityDescriptorArray();
99                 for (int i=0;i<sites.length;i++) {
100                         entityDescriptors.put(
101                                 sites[i].getEntityID(),
102                                 new XMLEntityDescriptorImpl(sites[i],parents));
103                 }
104                 EntitiesDescriptorType[] subgroups = group.getEntitiesDescriptorArray();
105                 for (int i=0;i<subgroups.length;i++) {
106                         processGroup(subgroups[i],parentgroups);
107                 }
108                 parentgroups.pop();
109         }
110
111         /**
112          * implement ...metadata.Metadata.lookup
113          * @param entityId ID of remote site
114          * @return EntityDescriptor cast as Provider to fulfill interface
115          */
116         public Provider lookup(String entityId) {
117                 return (EntityDescriptor) entityDescriptors.get(entityId);
118         }
119         
120         /**
121          * SAML 2 rename of lookup
122          * @param entityId ID of remote site
123          * @return EntityDescriptor of site
124          */
125         public EntityDescriptor getEntityDescriptor(String entityId) {
126                 return (EntityDescriptor) entityDescriptors.get(entityId);
127         }
128
129     /**
130      * implements ...metadata.Provider for XML data
131      * 
132      * <p>An object of this class is constructed for every 
133      * EntityDescriptor (site) in SAML 2 Metadata.
134      */
135     static private class XMLEntityDescriptorImpl extends EntityDescriptor {
136         
137         private EntityDescriptorType site;  // The XMLBean object
138         
139         private EntitiesDescriptorType[] groups; // ancestor elements
140         
141         private ProviderRole[] roles = null; // child roles             
142         
143         XMLEntityDescriptorImpl(
144                 EntityDescriptorType site, 
145                 EntitiesDescriptorType[] groups) {
146                 this.site=site;
147                 this.groups=groups;
148                 
149                 ArrayList/*<ProviderRoles>*/ roleArray = 
150                     new ArrayList/*<ProviderRoles>*/();
151                 
152                 /*
153                  * The rolesArray combines objects constructed from 
154                  * different types of roles. However, the implementing
155                  * objects must be constructed from the specific subtypes
156                  */
157                 
158                 AttributeAuthorityDescriptorType[] attributeAuthorityArray = 
159                     site.getAttributeAuthorityDescriptorArray();
160                 for (int i=0;i<attributeAuthorityArray.length;i++) {
161                         AttributeAuthorityRole aarole = 
162                                 new AttributeAuthorityRoleImpl(this,attributeAuthorityArray[i]);
163                         roleArray.add(aarole);
164                 }
165                 
166                 IDPSSODescriptorType[] handleServiceArray = site.getIDPSSODescriptorArray();
167                 for (int i=0;i<attributeAuthorityArray.length;i++) {
168                         IDPProviderRole idprole =
169                                 new IDPProviderRoleImpl(this,handleServiceArray[i]);
170                         roleArray.add(idprole);
171                 }
172                 
173                 // Put code to process more specific roles here as they are
174                 // needed by Shibboleth
175                 
176                 roles = new ProviderRole[roleArray.size()];
177                 Iterator iterator = roleArray.iterator();
178                 for (int i=0;i<roles.length;i++) {
179                     roles[i]= (ProviderRole) iterator.next();
180                 }
181         }
182         
183         public String getId() {
184                 return site.getEntityID();
185         }
186         
187         public String[] getGroups() {
188                 String [] groupnames = new String[groups.length];
189                 for (int i=0;i<groups.length;i++) {
190                         groupnames[i]=(groups[i]).getName();
191                 }
192                 return groupnames;
193         }
194     
195         public ContactPerson[] getContacts() {
196             // Create the interface objects on demand
197                 ContactType[] contacts = site.getContactPersonArray();
198                 XMLContactPersonImpl[] retarray = new XMLContactPersonImpl[contacts.length];
199                 for (int i=0;i<contacts.length;i++) {
200                         retarray[i]=new XMLContactPersonImpl(contacts[i]);
201                 }
202                 return retarray;
203         }
204     
205         public ProviderRole[] getRoles() {
206                 return roles;
207         }
208     }
209
210     /**
211      * implements ...metadata.ContactPerson for XML data
212      */
213     static private class XMLContactPersonImpl implements ContactPerson {
214         
215         ContactType contact; // Wrapped XMLBean object
216         
217         XMLContactPersonImpl(ContactType contact) {
218                 this.contact=contact;
219         }
220     
221         /*
222          * Dependency: the order of values in the XSD enumeration
223          * must match the order of values defined in the interface.
224          * [If someone objects, we can go back and get the string
225          * matching elseif logic.]
226          */
227         public int getType() {
228                 return contact.getContactType().intValue();
229         }
230     
231         public String getName() {
232                 return contact.getGivenName()+" "+contact.getSurName();
233         }
234     
235         public String[] getEmails() {
236                 return contact.getEmailAddressArray();
237         }
238     
239         public String[] getTelephones() {
240                 return contact.getTelephoneNumberArray();
241         }
242         
243     }
244
245     /**
246      * implements ...metadata.ProviderRole
247      * 
248      * <p>Represents a RoleDescriptor or (more commonly) one of its
249      * explicitly defined subtypes IDPSSO, SPSSO, AuthnAuthority, 
250      * PDP, AA, or AttributeConsumer).</p>
251      * 
252      * <p>We would make this class abstract, except that in theory
253      * somewhere down the line we may want to support the RoleDescriptor
254      * SAML 2 Metadata tag that allows new roles to be defined beyond
255      * the roles explicitly mentioned in the standard. Should that 
256      * occur, then the constructor for this class should become
257      * public. Now it is protected so you can only instantiate
258      * subclasses, but cannot create an object of this class directly.</p>
259      */
260     static private class XMLProviderRoleImpl 
261         implements ProviderRole {
262         
263         RoleDescriptorType roleDescriptorType = null;
264         
265         EntityDescriptor entity; // parent Entity
266         
267         private String name;
268         
269         Endpoint[] endpoints = null;
270         
271         String[] protocolUris = null;
272         
273         KeyDescriptor[] keyDescriptors= null;
274
275         protected XMLProviderRoleImpl(
276                 EntityDescriptor entity,
277                 RoleDescriptorType role) {
278                 this.entity=entity;
279                 this.roleDescriptorType = role;
280
281             List protocolSupportEnumeration = 
282                 roleDescriptorType.getProtocolSupportEnumeration();
283             protocolUris = new String[protocolSupportEnumeration.size()];
284             Iterator iterator = protocolSupportEnumeration.iterator();
285             for (int i=0;i<protocolUris.length;i++) {
286                 protocolUris[i]=(String) iterator.next();
287             }
288             
289             KeyDescriptorType[] keyDescriptorArray = 
290                 roleDescriptorType.getKeyDescriptorArray();
291             
292                 keyDescriptors = new KeyDescriptor[keyDescriptorArray.length];
293                 for (int i=0;i<keyDescriptorArray.length;i++) {
294                     keyDescriptors[i]= new KeyDescriptorImpl(keyDescriptorArray[i]); 
295                 }
296                 
297                 
298                 // The Endpoints types are specific to the subtypes
299                 // So the Endpoint array must be filled in by the
300                 // constructor of subclasses.
301         }
302     
303         public Provider getProvider() {
304                 return entity;
305         }
306     
307         public String[] getProtocolSupport() {
308                 return protocolUris;
309         }
310     
311         public boolean hasSupport(String version) {
312             return roleDescriptorType.getProtocolSupportEnumeration().contains(version);
313         }
314     
315         public ContactPerson[] getContacts() {
316             // Maybe we should return the contacts for the role???
317                 return entity.getContacts();
318         }
319     
320         public KeyDescriptor[] getKeyDescriptors() {
321                 return keyDescriptors;
322         }
323     
324         public Endpoint[] getDefaultEndpoints() {
325                 return endpoints;
326         }
327     
328         public URL getErrorURL() {
329                 try {
330                 return new URL(roleDescriptorType.getErrorURL());
331             } catch (MalformedURLException e) {
332                 return null;
333            }
334         }
335     
336     }
337
338     /**
339      * implements ...metadata.Endpoint for XML data
340      * 
341      * <p>Delegate calls to the XMLBean EndpointType</p>
342      */
343     static private class XMLEndpointImpl implements Endpoint {
344         
345         EndpointType endpoint;
346         
347         XMLEndpointImpl(EndpointType xmlbean) {
348             this.endpoint=xmlbean;
349         }
350     
351         public String getBinding() {
352                 return endpoint.getBinding();
353         }
354     
355         public String getLocation() {
356                 return endpoint.getLocation();
357         }
358     
359         public String getResponseLocation() {
360                 return endpoint.getResponseLocation();
361         }
362         
363     }
364
365     /**
366      * A subtype of generic roles for AttributeAuthority entries.
367      */
368     private static class AttributeAuthorityRoleImpl
369         extends XMLProviderRoleImpl
370         implements AttributeAuthorityRole {
371     
372         // Yes, this is redundant with the parent class reference 
373         // to the same object as a generic RoleDescriptorType, but
374         // having a more specific field saves casting that field
375         // all the time in this code.
376         AttributeAuthorityDescriptorType aabean;
377     
378         public AttributeAuthorityRoleImpl(
379                 XMLEntityDescriptorImpl impl, 
380                 AttributeAuthorityDescriptorType aaDescriptor) {
381             
382             super(impl,aaDescriptor);
383             aabean=aaDescriptor;
384             
385             EndpointType[] attributeServiceArray = 
386                 aaDescriptor.getAttributeServiceArray();
387             endpoints = new Endpoint[attributeServiceArray.length];
388             for (int i=0;i<attributeServiceArray.length;i++) {
389                 endpoints[i]=new XMLEndpointImpl(attributeServiceArray[i]);
390             }
391         }
392
393         public Endpoint[] getAttributeServices() {
394                 return endpoints;
395         }
396     
397         public SAMLAttributeDesignator[] getAttributeDesignators() {
398                 return null;
399         }
400     }
401
402     /**
403      * A subtype of generic roles for Handle Server entries.
404      */
405     private static class IDPProviderRoleImpl
406         extends XMLProviderRoleImpl
407         implements IDPProviderRole {
408     
409         IDPSSODescriptorType hsRole;
410         
411         public IDPProviderRoleImpl(XMLEntityDescriptorImpl impl, 
412                 IDPSSODescriptorType type) {
413             super(impl,type);
414             hsRole = type;
415             
416             EndpointType[] singleSignOnServiceArray = 
417                 type.getSingleSignOnServiceArray();
418             endpoints = new Endpoint[singleSignOnServiceArray.length];
419             for (int i=0;i<singleSignOnServiceArray.length;i++) {
420                 endpoints[i]=new XMLEndpointImpl(singleSignOnServiceArray[i]);
421             }
422         }
423     }
424     
425     private static class KeyDescriptorImpl 
426         implements KeyDescriptor {
427         
428         KeyDescriptorType keyDescriptor = null;
429         
430         public KeyDescriptorImpl(KeyDescriptorType keyDescriptor) {
431             super();
432             this.keyDescriptor = keyDescriptor;
433         }
434         
435         public String[] getEncryptionMethod() {
436             EncryptionMethodType[] encryptionMethodArray = 
437                 keyDescriptor.getEncryptionMethodArray();
438             String[] methods = new String[encryptionMethodArray.length];
439             for (int i=0;i<encryptionMethodArray.length;i++) {
440                     EncryptionMethodType encryptionMethod = encryptionMethodArray[i];
441                     methods[i] =encryptionMethod.getAlgorithm();
442             }
443             return methods;
444         }
445         public KeyInfo[] getKeyInfo() {
446             Node fragment = keyDescriptor.getKeyInfo().newDomNode();
447             Element node = (Element) fragment.getFirstChild();
448             KeyInfo info = null;
449             try {
450                 info = new KeyInfo(node,"");
451             } catch (XMLSecurityException e) {
452                 return null;
453             }
454             return new KeyInfo[] {info};
455         }
456         
457         public int getUse() {
458             String value = keyDescriptor.getUse().toString();
459             if (value.equals("encryption"))
460                 return KeyDescriptor.ENCRYPTION;
461             else
462                 return KeyDescriptor.SIGNING;
463         }
464 }
465 }