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 above
6 * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7 * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8 * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
9 * 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, nor
11 * 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 contact
13 * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14 * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15 * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16 * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18 * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19 * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 package edu.internet2.middleware.shibboleth.idp;
30 import java.security.Principal;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Iterator;
35 import javax.servlet.http.HttpServletRequest;
37 import org.apache.log4j.Logger;
38 import org.apache.xml.security.signature.XMLSignature;
39 import org.opensaml.InvalidCryptoException;
40 import org.opensaml.SAMLAssertion;
41 import org.opensaml.SAMLAttribute;
42 import org.opensaml.SAMLException;
43 import org.opensaml.SAMLResponse;
44 import org.opensaml.artifact.Artifact;
45 import org.w3c.dom.Element;
47 import edu.internet2.middleware.shibboleth.aa.AAAttribute;
48 import edu.internet2.middleware.shibboleth.aa.AAAttributeSet;
49 import edu.internet2.middleware.shibboleth.aa.AAException;
50 import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
51 import edu.internet2.middleware.shibboleth.aa.arp.ArpProcessingException;
52 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
53 import edu.internet2.middleware.shibboleth.artifact.ArtifactMapper;
54 import edu.internet2.middleware.shibboleth.artifact.provider.MemoryArtifactMapper;
55 import edu.internet2.middleware.shibboleth.common.Credential;
56 import edu.internet2.middleware.shibboleth.common.NameMapper;
57 import edu.internet2.middleware.shibboleth.common.RelyingParty;
58 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
59 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
60 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
61 import edu.internet2.middleware.shibboleth.metadata.Metadata;
62 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
65 * Delivers core IdP functionality (Attribute resolution, ARP filtering, Metadata lookup, Signing, Mapping between local &
66 * SAML identifiers, etc.) to components that process protocol-specific requests.
68 * @author Walter Hoehn
70 public class IdPProtocolSupport implements Metadata {
72 private static Logger log = Logger.getLogger(IdPProtocolSupport.class.getName());
73 private Logger transactionLog;
74 private IdPConfig config;
75 private ArrayList fedMetadata = new ArrayList();
76 private NameMapper nameMapper;
77 private ServiceProviderMapper spMapper;
78 private ArpEngine arpEngine;
79 private AttributeResolver resolver;
80 private ArtifactMapper artifactMapper;
81 private Semaphore throttle;
83 IdPProtocolSupport(IdPConfig config, Logger transactionLog, NameMapper nameMapper, ServiceProviderMapper spMapper,
84 ArpEngine arpEngine, AttributeResolver resolver) throws ShibbolethConfigurationException {
86 this.transactionLog = transactionLog;
88 this.nameMapper = nameMapper;
89 this.spMapper = spMapper;
90 spMapper.setMetadata(this);
91 this.arpEngine = arpEngine;
92 this.resolver = resolver;
93 // TODO make this pluggable... and clean up memory impl
94 artifactMapper = new MemoryArtifactMapper();
96 // Load a semaphore that throttles how many requests the IdP will handle at once
97 throttle = new Semaphore(config.getMaxThreads());
100 public static void validateEngineData(HttpServletRequest req) throws InvalidClientDataException {
102 // TODO this should be pulled out into handlers
104 if ((req.getRemoteAddr() == null) || (req.getRemoteAddr().equals(""))) { throw new InvalidClientDataException(
105 "Unable to obtain client address."); }
108 public Logger getTransactionLog() {
110 return transactionLog;
113 public IdPConfig getIdPConfig() {
118 public NameMapper getNameMapper() {
123 public ServiceProviderMapper getServiceProviderMapper() {
128 public void signAssertions(SAMLAssertion[] assertions, RelyingParty relyingParty) throws InvalidCryptoException,
131 if (relyingParty.getIdentityProvider().getSigningCredential() == null
132 || relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() == null) {
136 for (int i = 0; i < assertions.length; i++) {
137 String assertionAlgorithm;
138 if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
139 assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
140 } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
141 assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
143 throw new InvalidCryptoException(SAMLException.RESPONDER,
144 "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
149 assertions[i].sign(assertionAlgorithm, relyingParty.getIdentityProvider().getSigningCredential()
150 .getPrivateKey(), Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential()
151 .getX509CertificateChain()));
158 public void signResponse(SAMLResponse response, RelyingParty relyingParty) throws SAMLException {
160 // Make sure we have an appropriate credential
161 if (relyingParty.getIdentityProvider().getSigningCredential() == null
162 || relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() == null) {
168 String responseAlgorithm;
169 if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
170 responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
171 } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
172 responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
174 throw new InvalidCryptoException(SAMLException.RESPONDER,
175 "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
179 response.sign(responseAlgorithm, relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey(),
180 Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential().getX509CertificateChain()));
186 protected void addFederationProvider(Element element) {
188 log.debug("Found Federation Provider configuration element.");
189 if (!element.getTagName().equals("FederationProvider")) {
190 log.error("Error while attemtping to load Federation Provider. Malformed provider specificaion.");
195 fedMetadata.add(FederationProviderFactory.loadProvider(element));
196 } catch (MetadataException e) {
197 log.error("Unable to load Federation Provider. Skipping...");
201 public int providerCount() {
203 return fedMetadata.size();
206 public EntityDescriptor lookup(String providerId) {
208 Iterator iterator = fedMetadata.iterator();
209 while (iterator.hasNext()) {
210 EntityDescriptor provider = ((Metadata) iterator.next()).lookup(providerId);
211 if (provider != null) { return provider; }
216 public EntityDescriptor lookup(Artifact artifact) {
218 Iterator iterator = fedMetadata.iterator();
219 while (iterator.hasNext()) {
220 EntityDescriptor provider = ((Metadata) iterator.next()).lookup(artifact);
221 if (provider != null) { return provider; }
226 public SAMLAttribute[] getReleaseAttributes(Principal principal, String requester, URL resource) throws AAException {
229 URI[] potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester, resource);
230 return getReleaseAttributes(principal, requester, resource, potentialAttributes);
232 } catch (ArpProcessingException e) {
233 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
235 throw new AAException("Error retrieving data for principal.");
239 public SAMLAttribute[] getReleaseAttributes(Principal principal, String requester, URL resource,
240 URI[] attributeNames) throws AAException {
243 AAAttributeSet attributeSet = new AAAttributeSet();
244 for (int i = 0; i < attributeNames.length; i++) {
245 AAAttribute attribute = new AAAttribute(attributeNames[i].toString());
246 attributeSet.add(attribute);
249 return resolveAttributes(principal, requester, resource, attributeSet);
251 } catch (SAMLException e) {
252 log.error("An error occurred while creating attributes for principal (" + principal.getName() + ") :"
254 throw new AAException("Error retrieving data for principal.");
256 } catch (ArpProcessingException e) {
257 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
259 throw new AAException("Error retrieving data for principal.");
263 private SAMLAttribute[] resolveAttributes(Principal principal, String requester, URL resource,
264 AAAttributeSet attributeSet) throws ArpProcessingException {
266 resolver.resolveAttributes(principal, requester, attributeSet);
267 arpEngine.filterAttributes(attributeSet, principal, requester, resource);
268 return attributeSet.getAttributes();
272 * Cleanup resources that won't be released when this object is garbage-collected
274 public void destroy() {
280 public ArtifactMapper getArtifactMapper() {
282 return artifactMapper;
285 private class Semaphore {
289 public Semaphore(int value) {
294 public synchronized void enter() {
300 } catch (InterruptedException e) {
301 // squelch and continue
306 public synchronized void exit() {