2 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp;
21 import java.security.Principal;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Iterator;
26 import org.apache.log4j.Logger;
27 import org.apache.xml.security.signature.XMLSignature;
28 import org.opensaml.InvalidCryptoException;
29 import org.opensaml.SAMLAssertion;
30 import org.opensaml.SAMLAttribute;
31 import org.opensaml.SAMLException;
32 import org.opensaml.SAMLResponse;
33 import org.opensaml.artifact.Artifact;
34 import org.w3c.dom.Element;
36 import edu.internet2.middleware.shibboleth.aa.AAAttribute;
37 import edu.internet2.middleware.shibboleth.aa.AAAttributeSet;
38 import edu.internet2.middleware.shibboleth.aa.AAException;
39 import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
40 import edu.internet2.middleware.shibboleth.aa.arp.ArpProcessingException;
41 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
42 import edu.internet2.middleware.shibboleth.artifact.ArtifactMapper;
43 import edu.internet2.middleware.shibboleth.common.Credential;
44 import edu.internet2.middleware.shibboleth.common.NameMapper;
45 import edu.internet2.middleware.shibboleth.common.RelyingParty;
46 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
47 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
48 import edu.internet2.middleware.shibboleth.common.provider.ShibbolethTrust;
49 import edu.internet2.middleware.shibboleth.common.Trust;
50 import edu.internet2.middleware.shibboleth.metadata.EntitiesDescriptor;
51 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
52 import edu.internet2.middleware.shibboleth.metadata.Metadata;
53 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
56 * Delivers core IdP functionality (Attribute resolution, ARP filtering, Metadata lookup, Signing, Mapping between local &
57 * SAML identifiers, etc.) to components that process protocol-specific requests.
59 * @author Walter Hoehn
61 public class IdPProtocolSupport implements Metadata {
63 private static Logger log = Logger.getLogger(IdPProtocolSupport.class.getName());
64 private Logger transactionLog;
65 private IdPConfig config;
66 private ArrayList metadata = new ArrayList();
67 private NameMapper nameMapper;
68 private ServiceProviderMapper spMapper;
69 private ArpEngine arpEngine;
70 private AttributeResolver resolver;
71 private ArtifactMapper artifactMapper;
72 private Semaphore throttle;
73 private Trust trust = new ShibbolethTrust();
75 IdPProtocolSupport(IdPConfig config, Logger transactionLog, NameMapper nameMapper, ServiceProviderMapper spMapper,
76 ArpEngine arpEngine, AttributeResolver resolver, ArtifactMapper artifactMapper)
77 throws ShibbolethConfigurationException {
79 this.transactionLog = transactionLog;
81 this.nameMapper = nameMapper;
82 this.spMapper = spMapper;
83 spMapper.setMetadata(this);
84 this.arpEngine = arpEngine;
85 this.resolver = resolver;
86 this.artifactMapper = artifactMapper;
88 // Load a semaphore that throttles how many requests the IdP will handle at once
89 throttle = new Semaphore(config.getMaxThreads());
92 public Logger getTransactionLog() {
94 return transactionLog;
97 public IdPConfig getIdPConfig() {
102 public NameMapper getNameMapper() {
107 public ServiceProviderMapper getServiceProviderMapper() {
112 public void signAssertions(SAMLAssertion[] assertions, RelyingParty relyingParty) throws InvalidCryptoException,
115 if (relyingParty.getIdentityProvider().getSigningCredential() == null
116 || relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() == null) { throw new InvalidCryptoException(
117 SAMLException.RESPONDER, "Invalid signing credential."); }
119 for (int i = 0; i < assertions.length; i++) {
120 String assertionAlgorithm;
121 if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
122 assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
123 } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
124 assertionAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
126 throw new InvalidCryptoException(SAMLException.RESPONDER,
127 "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
132 assertions[i].sign(assertionAlgorithm, relyingParty.getIdentityProvider().getSigningCredential()
133 .getPrivateKey(), Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential()
134 .getX509CertificateChain()));
141 public void signResponse(SAMLResponse response, RelyingParty relyingParty) throws SAMLException {
143 // Make sure we have an appropriate credential
144 if (relyingParty.getIdentityProvider().getSigningCredential() == null
145 || relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() == null) { throw new InvalidCryptoException(
146 SAMLException.RESPONDER, "Invalid signing credential."); }
149 String responseAlgorithm;
150 if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.RSA) {
151 responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
152 } else if (relyingParty.getIdentityProvider().getSigningCredential().getCredentialType() == Credential.DSA) {
153 responseAlgorithm = XMLSignature.ALGO_ID_SIGNATURE_DSA;
155 throw new InvalidCryptoException(SAMLException.RESPONDER,
156 "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
160 response.sign(responseAlgorithm, relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey(),
161 Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential().getX509CertificateChain()));
167 protected void addMetadataProvider(Element element) {
169 log.debug("Found Metadata Provider configuration element.");
170 if (!element.getTagName().equals("MetadataProvider")) {
171 log.error("Error while attemtping to load Metadata Provider. Malformed provider specificaion.");
176 metadata.add(MetadataProviderFactory.loadProvider(element));
177 } catch (MetadataException e) {
178 log.error("Unable to load Metadata Provider. Skipping...");
182 public int providerCount() {
184 return metadata.size();
187 public EntityDescriptor lookup(String providerId, boolean strict) {
189 Iterator iterator = metadata.iterator();
190 while (iterator.hasNext()) {
191 EntityDescriptor provider = ((Metadata) iterator.next()).lookup(providerId);
192 if (provider != null) { return provider; }
197 public EntityDescriptor lookup(Artifact artifact, boolean strict) {
199 Iterator iterator = metadata.iterator();
200 while (iterator.hasNext()) {
201 EntityDescriptor provider = ((Metadata) iterator.next()).lookup(artifact);
202 if (provider != null) { return provider; }
207 public EntityDescriptor lookup(String id) {
208 return lookup(id,true);
211 public EntityDescriptor lookup(Artifact artifact) {
212 return lookup(artifact,true);
215 public EntityDescriptor getRootEntity() {
219 public EntitiesDescriptor getRootEntities() {
223 public SAMLAttribute[] getReleaseAttributes(Principal principal, RelyingParty relyingParty, String requester,
224 URL resource) throws AAException {
227 URI[] potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester, resource);
228 return getReleaseAttributes(principal, relyingParty, requester, resource, potentialAttributes);
230 } catch (ArpProcessingException e) {
231 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
233 throw new AAException("Error retrieving data for principal.");
237 public SAMLAttribute[] getReleaseAttributes(Principal principal, RelyingParty relyingParty, String requester,
238 URL resource, URI[] attributeNames) throws AAException {
241 AAAttributeSet attributeSet = new AAAttributeSet();
242 for (int i = 0; i < attributeNames.length; i++) {
244 AAAttribute attribute = null;
245 if (relyingParty.wantsSchemaHack()) {
246 attribute = new AAAttribute(attributeNames[i].toString(), true);
248 attribute = new AAAttribute(attributeNames[i].toString(), false);
251 attributeSet.add(attribute);
254 return resolveAttributes(principal, requester, relyingParty.getIdentityProvider().getProviderId(),
255 resource, attributeSet);
257 } catch (SAMLException e) {
258 log.error("An error occurred while creating attributes for principal (" + principal.getName() + ") :"
260 throw new AAException("Error retrieving data for principal.");
262 } catch (ArpProcessingException e) {
263 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
265 throw new AAException("Error retrieving data for principal.");
269 private SAMLAttribute[] resolveAttributes(Principal principal, String requester, String responder, URL resource,
270 AAAttributeSet attributeSet) throws ArpProcessingException {
272 resolver.resolveAttributes(principal, requester, responder, attributeSet);
273 arpEngine.filterAttributes(attributeSet, principal, requester, resource);
274 return attributeSet.getAttributes();
278 * Cleanup resources that won't be released when this object is garbage-collected
280 public void destroy() {
286 public ArtifactMapper getArtifactMapper() {
288 return artifactMapper;
291 public Trust getTrust() {
296 private class Semaphore {
300 public Semaphore(int value) {
305 public synchronized void enter() {
311 } catch (InterruptedException e) {
312 // squelch and continue
317 public synchronized void exit() {