81563e7d53994690b5dedab8352bad0bb6561b15
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / metadata / provider / XMLMetadata.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the
6  * above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
7  * materials provided with the distribution, if any, must include the following acknowledgment: "This product includes
8  * software developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2
9  * Project. Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2,
11  * nor the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please
13  * contact shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2,
14  * UCAID, or the University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name,
15  * without prior written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS
16  * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
18  * NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS
19  * WITH LICENSEE. IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED
20  * INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
23  * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 package edu.internet2.middleware.shibboleth.metadata.provider;
28
29 import java.net.URL;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.Stack;
34
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
37
38 import org.apache.log4j.Logger;
39 import org.apache.xml.security.Init;
40 import org.apache.xml.security.keys.KeyInfo;
41 import org.w3c.dom.Element;
42 import org.w3c.dom.Node;
43 import org.w3c.dom.NodeList;
44
45 import edu.internet2.middleware.shibboleth.common.XML;
46 import edu.internet2.middleware.shibboleth.metadata.AttributeConsumerRole;
47 import edu.internet2.middleware.shibboleth.metadata.ContactPerson;
48 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
49 import edu.internet2.middleware.shibboleth.metadata.KeyDescriptor;
50 import edu.internet2.middleware.shibboleth.metadata.Metadata;
51 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
52 import edu.internet2.middleware.shibboleth.metadata.Provider;
53 import edu.internet2.middleware.shibboleth.metadata.ProviderRole;
54 import edu.internet2.middleware.shibboleth.metadata.SPProviderRole;
55
56 /**
57  * @author Walter Hoehn (wassa@columbia.edu)
58  */
59 public class XMLMetadata implements Metadata {
60
61         private static Logger           log                     = Logger.getLogger(XMLMetadata.class.getName());
62         public static final String      namespace       = "urn:mace:shibboleth:1.0";
63         private Map                                     providers       = new HashMap();
64
65         public XMLMetadata(Element root) throws MetadataException {
66                 try {
67                         new ShibGroup(root, new Stack(), providers);
68                 } catch (XMLMetadataException e) {
69                         log.error("Encountered a problem loading federation metadata: " + e);
70                         throw new MetadataException("Unable to load federation metadata.");
71                 }
72         }
73
74         public Provider lookup(String providerId) {
75                 if (providers.containsKey(providerId)) {
76                         return (Provider) providers.get(providerId);
77                 }
78                 return null;
79         }
80
81         private class ShibGroup {
82
83                 private String  id;
84
85                 ShibGroup(Element root, Stack parents, Map providers) throws XMLMetadataException {
86                         if (!root.getNodeName().equals("SiteGroup")) {
87                                 throw new XMLMetadataException("Excpected \"SiteGroup\", found \"" + root.getNodeName() + "\".");
88                         }
89
90                         id = root.getAttribute("Name");
91                         if (id == null || id.equals("")) {
92                                 throw new XMLMetadataException("A name must be specified for the site group.");
93                         }
94
95                         parents.push(id);
96                         NodeList nodes = root.getChildNodes();
97                         for (int i = 0; nodes.getLength() > i; i++) {
98                                 if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
99
100                                         if (nodes.item(i).getNodeName().equals("SiteGroup")) {
101                                                 new ShibGroup((Element) nodes.item(i), parents, providers);
102
103                                         } else if (nodes.item(i).getNodeName().equals("DestinationSite")) {
104
105                                                 Provider provider = new ShibTargetXMLProvider((Element) nodes.item(i), (String[]) parents
106                                                                 .toArray(new String[0]));
107                                                 providers.put(provider.getId(), provider);
108
109                                         } else if (nodes.item(i).getNodeName().equals("OriginSite")) {
110                                                 log.debug("Ignoring OriginSite.");
111                                         }
112                                 }
113                         }
114                         parents.pop();
115                 }
116         }
117
118         class ShibTargetXMLProvider implements Provider, ProviderRole, SPProviderRole, AttributeConsumerRole {
119
120                 private String          id;
121                 private HashSet         contacts;
122                 private String[]        groups;
123                 private HashSet         assertionConsumers      = new HashSet();
124                 private HashSet         keyDescriptors          = new HashSet();
125
126                 ShibTargetXMLProvider(Element element, String[] groups) throws XMLMetadataException {
127                         if (!element.getNodeName().equals("DestinationSite")) {
128                                 log.error("This provider implementation can only marshall Shibboleth target metadata.");
129                                 throw new XMLMetadataException("Unable to load provider.");
130                         }
131
132                         this.groups = groups;
133
134                         id = element.getAttribute("Name");
135                         if (id == null || id.equals("")) {
136                                 log.error("No name set for provider.");
137                                 throw new XMLMetadataException("Unable to load provider.");
138                         }
139
140                         NodeList contactNodes = element.getElementsByTagNameNS(namespace, "Contact");
141                         if (contactNodes.getLength() > 0) {
142                                 contacts = new HashSet();
143                         }
144                         for (int i = 0; contactNodes.getLength() > i; i++) {
145                                 try {
146                                         contacts.add(new XMLContactPerson((Element) contactNodes.item(i)));
147                                 } catch (XMLMetadataException e) {
148                                         log.error("Error loading parsing contact person for provider (" + id + "): " + e.getMessage());
149                                 }
150                         }
151
152                         NodeList consumerNodes = element.getElementsByTagNameNS(namespace, "AssertionConsumerServiceURL");
153                         for (int i = 0; consumerNodes.getLength() > i; i++) {
154                                 String location = ((Element) consumerNodes.item(i)).getAttribute("Location");
155                                 if (location == null || location.equals("")) {
156                                         log.error("Destination site (" + id + ") contained a malformed Assertion Consumer Service URL.");
157                                         continue;
158                                 }
159                                 assertionConsumers.add(new ShibEndpoint(location));
160                         }
161                         if (assertionConsumers.size() == 0) {
162                                 log.error("No assertion consumer URLs specified for this provider.");
163                                 throw new XMLMetadataException("Unable to load provider.");
164                         }
165
166                         NodeList requesterNodes = element.getElementsByTagNameNS(namespace, "AttributeRequester");
167                         for (int i = 0; requesterNodes.getLength() > i; i++) {
168                                 String name = ((Element) requesterNodes.item(i)).getAttribute("Name");
169                                 if (name == null || name.equals("")) {
170                                         log.error("Destination site (" + id + ") contained a malformed Attribute Requester name.");
171                                         continue;
172                                 }
173
174                                 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
175                                 try {
176                                         if (!Init.isInitialized()) {
177                                                 org.apache.xml.security.Init.init();
178                                         }
179                                         KeyInfo keyInfo = new KeyInfo(docFactory.newDocumentBuilder().newDocument());
180                                         keyInfo.addKeyName(name);
181                                         keyDescriptors.add(new TargetKeyDescriptor(keyInfo));
182
183                                 } catch (ParserConfigurationException e) {
184                                         log.error("Unable to create xml document needed for KeyInfo.");
185                                 }
186                         }
187                         if (keyDescriptors.size() == 0) {
188                                 log.error("No valid attribute requesters specified for this provider.");
189                                 throw new XMLMetadataException("Unable to load provider.");
190                         }
191
192                 }
193
194                 public String getId() {
195                         return id;
196                 }
197
198                 public String[] getGroups() {
199                         return groups;
200                 }
201
202                 public ContactPerson[] getContacts() {
203                         if (contacts != null) {
204                                 return (ContactPerson[]) contacts.toArray(new ContactPerson[0]);
205                         }
206                         return new ContactPerson[0];
207                 }
208
209                 public ProviderRole[] getRoles() {
210                         return new ProviderRole[]{this};
211                 }
212
213                 public Provider getProvider() {
214                         return this;
215                 }
216
217                 public String[] getProtocolSupport() {
218                         return new String[]{XML.SHIB_NS};
219                 }
220
221                 public boolean hasSupport(String version) {
222                         if (version.equals(XML.SHIB_NS)) {
223                                 return true;
224                         } else {
225                                 return false;
226                         }
227                 }
228
229                 public Endpoint[] getDefaultEndpoints() {
230                         return new Endpoint[0];
231                 }
232
233                 public URL getErrorURL() {
234                         return null;
235                 }
236
237                 public boolean getAuthnRequestsSigned() {
238                         return true;
239                 }
240
241                 public Endpoint[] getAssertionConsumerServiceURLs() {
242                         return (Endpoint[]) assertionConsumers.toArray(new Endpoint[0]);
243                 }
244
245                 public KeyDescriptor[] getKeyDescriptors() {
246                         return (KeyDescriptor[]) keyDescriptors.toArray(new KeyDescriptor[0]);
247                 }
248                 
249                 public boolean wantAssertionsSigned() {
250                         return false;
251                 }
252
253                 class ShibEndpoint implements Endpoint {
254
255                         private String  binding;
256                         private String  location;
257
258                         ShibEndpoint(String location) {
259                                 this.location = location;
260                         }
261
262                         public String getBinding() {
263                                 return XML.SHIB_NS;
264                         }
265
266                         public String getVersion() {
267                                 return null;
268                         }
269
270                         public String getLocation() {
271                                 return location;
272                         }
273
274                         public String getResponseLocation() {
275                                 return null;
276                         }
277                 }
278
279                 class TargetKeyDescriptor implements KeyDescriptor {
280
281                         private KeyInfo keyInfo;
282
283                         TargetKeyDescriptor(KeyInfo keyInfo) {
284                                 this.keyInfo = keyInfo;
285                         }
286
287                         public int getUse() {
288                                 return ENCRYPTION;
289                         }
290
291                         public String[] getEncryptionMethod() {
292                                 return null;
293                         }
294
295                         public int getKeySize() {
296                                 return 0;
297                         }
298
299                         public KeyInfo[] getKeyInfo() {
300                                 return new KeyInfo[]{keyInfo};
301                         }
302                 }
303         }
304
305         class XMLContactPerson implements ContactPerson {
306
307                 private int             type;
308                 private String  name;
309                 private String  email;
310
311                 public XMLContactPerson(Element element) throws XMLMetadataException {
312                         String rawType = element.getAttribute("Type");
313                         if (rawType.equalsIgnoreCase("TECHNICAL")) {
314                                 type = ContactPerson.TECHNICAL;
315                         } else if (rawType.equalsIgnoreCase("SUPPORT")) {
316                                 type = ContactPerson.SUPPORT;
317                         } else if (rawType.equalsIgnoreCase("ADMINISTRATIVE")) {
318                                 type = ContactPerson.ADMINISTRATIVE;
319                         } else if (rawType.equalsIgnoreCase("BILLING")) {
320                                 type = ContactPerson.BILLING;
321                         } else if (rawType.equalsIgnoreCase("OTHER")) {
322                                 type = ContactPerson.OTHER;
323                         } else {
324                                 throw new XMLMetadataException("Unknown contact type.");
325                         }
326                         name = element.getAttribute("Name");
327                         if (name == null || name.equals("")) {
328                                 throw new XMLMetadataException("No contact name.");
329                         }
330                         email = element.getAttribute("Email");
331                 }
332
333                 public int getType() {
334                         return type;
335                 }
336
337                 public String getName() {
338                         return name;
339                 }
340
341                 public String[] getEmails() {
342                         if (email != null & !email.equals("")) {
343                                 return new String[]{email};
344                         }
345                         return new String[0];
346                 }
347
348                 public String[] getTelephones() {
349                         return new String[0];
350                 }
351         }
352
353         class XMLMetadataException extends Exception {
354
355                 XMLMetadataException(String message) {
356                         super(message);
357                 }
358         }
359 }