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.
27 package edu.internet2.middleware.shibboleth.metadata.provider;
30 import java.util.HashMap;
31 import java.util.HashSet;
33 import java.util.Stack;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.parsers.ParserConfigurationException;
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;
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;
57 * @author Walter Hoehn (wassa@columbia.edu)
59 public class XMLMetadata implements Metadata {
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();
65 public XMLMetadata(Element root) throws MetadataException {
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.");
74 public Provider lookup(String providerId) {
75 if (providers.containsKey(providerId)) {
76 return (Provider) providers.get(providerId);
81 private class ShibGroup {
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() + "\".");
90 id = root.getAttribute("Name");
91 if (id == null || id.equals("")) {
92 throw new XMLMetadataException("A name must be specified for the site group.");
96 NodeList nodes = root.getChildNodes();
97 for (int i = 0; nodes.getLength() > i; i++) {
98 if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
100 if (nodes.item(i).getNodeName().equals("SiteGroup")) {
101 new ShibGroup((Element) nodes.item(i), parents, providers);
103 } else if (nodes.item(i).getNodeName().equals("DestinationSite")) {
105 Provider provider = new ShibTargetXMLProvider((Element) nodes.item(i), (String[]) parents
106 .toArray(new String[0]));
107 providers.put(provider.getId(), provider);
109 } else if (nodes.item(i).getNodeName().equals("OriginSite")) {
110 log.debug("Ignoring OriginSite.");
118 class ShibTargetXMLProvider implements Provider, ProviderRole, SPProviderRole, AttributeConsumerRole {
121 private HashSet contacts;
122 private String[] groups;
123 private HashSet assertionConsumers = new HashSet();
124 private HashSet keyDescriptors = new HashSet();
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.");
132 this.groups = groups;
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.");
140 NodeList contactNodes = element.getElementsByTagNameNS(namespace, "Contact");
141 if (contactNodes.getLength() > 0) {
142 contacts = new HashSet();
144 for (int i = 0; contactNodes.getLength() > i; i++) {
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());
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.");
159 assertionConsumers.add(new ShibEndpoint(location));
161 if (assertionConsumers.size() == 0) {
162 log.error("No assertion consumer URLs specified for this provider.");
163 throw new XMLMetadataException("Unable to load provider.");
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.");
174 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
176 if (!Init.isInitialized()) {
177 org.apache.xml.security.Init.init();
179 KeyInfo keyInfo = new KeyInfo(docFactory.newDocumentBuilder().newDocument());
180 keyInfo.addKeyName(name);
181 keyDescriptors.add(new TargetKeyDescriptor(keyInfo));
183 } catch (ParserConfigurationException e) {
184 log.error("Unable to create xml document needed for KeyInfo.");
187 if (keyDescriptors.size() == 0) {
188 log.error("No valid attribute requesters specified for this provider.");
189 throw new XMLMetadataException("Unable to load provider.");
194 public String getId() {
198 public String[] getGroups() {
202 public ContactPerson[] getContacts() {
203 if (contacts != null) {
204 return (ContactPerson[]) contacts.toArray(new ContactPerson[0]);
206 return new ContactPerson[0];
209 public ProviderRole[] getRoles() {
210 return new ProviderRole[]{this};
213 public Provider getProvider() {
217 public String[] getProtocolSupport() {
218 return new String[]{XML.SHIB_NS};
221 public boolean hasSupport(String version) {
222 if (version.equals(XML.SHIB_NS)) {
229 public Endpoint[] getDefaultEndpoints() {
230 return new Endpoint[0];
233 public URL getErrorURL() {
237 public boolean getAuthnRequestsSigned() {
241 public Endpoint[] getAssertionConsumerServiceURLs() {
242 return (Endpoint[]) assertionConsumers.toArray(new Endpoint[0]);
245 public KeyDescriptor[] getKeyDescriptors() {
246 return (KeyDescriptor[]) keyDescriptors.toArray(new KeyDescriptor[0]);
249 class ShibEndpoint implements Endpoint {
251 private String binding;
252 private String location;
254 ShibEndpoint(String location) {
255 this.location = location;
258 public String getBinding() {
262 public String getVersion() {
266 public String getLocation() {
270 public String getResponseLocation() {
275 class TargetKeyDescriptor implements KeyDescriptor {
277 private KeyInfo keyInfo;
279 TargetKeyDescriptor(KeyInfo keyInfo) {
280 this.keyInfo = keyInfo;
283 public int getUse() {
287 public String[] getEncryptionMethod() {
291 public int getKeySize() {
295 public KeyInfo[] getKeyInfo() {
296 return new KeyInfo[]{keyInfo};
302 class XMLContactPerson implements ContactPerson {
306 private String email;
308 public XMLContactPerson(Element element) throws XMLMetadataException {
309 String rawType = element.getAttribute("Type");
310 if (rawType.equalsIgnoreCase("TECHNICAL")) {
311 type = ContactPerson.TECHNICAL;
312 } else if (rawType.equalsIgnoreCase("SUPPORT")) {
313 type = ContactPerson.SUPPORT;
314 } else if (rawType.equalsIgnoreCase("ADMINISTRATIVE")) {
315 type = ContactPerson.ADMINISTRATIVE;
316 } else if (rawType.equalsIgnoreCase("BILLING")) {
317 type = ContactPerson.BILLING;
318 } else if (rawType.equalsIgnoreCase("OTHER")) {
319 type = ContactPerson.OTHER;
321 throw new XMLMetadataException("Unknown contact type.");
323 name = element.getAttribute("Name");
324 if (name == null || name.equals("")) {
325 throw new XMLMetadataException("No contact name.");
327 email = element.getAttribute("Email");
330 public int getType() {
334 public String getName() {
338 public String[] getEmails() {
339 if (email != null & !email.equals("")) {
340 return new String[]{email};
342 return new String[0];
345 public String[] getTelephones() {
346 return new String[0];
350 class XMLMetadataException extends Exception {
352 XMLMetadataException(String message) {