2 * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.] Licensed under the Apache License,
3 * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy
4 * of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in
5 * writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
6 * OF ANY KIND, either express or implied. See the License for the specific language governing permissions and
7 * limitations under the License.
10 package edu.internet2.middleware.shibboleth.idp;
13 import java.security.Principal;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.List;
20 import javax.xml.namespace.QName;
22 import org.apache.log4j.Logger;
23 import org.apache.xml.security.signature.XMLSignature;
24 import org.opensaml.InvalidCryptoException;
25 import org.opensaml.SAMLAssertion;
26 import org.opensaml.SAMLAttribute;
27 import org.opensaml.SAMLException;
28 import org.opensaml.SAMLResponse;
29 import org.opensaml.saml2.metadata.EntitiesDescriptor;
30 import org.opensaml.saml2.metadata.EntityDescriptor;
31 import org.opensaml.saml2.metadata.RoleDescriptor;
32 import org.opensaml.saml2.metadata.provider.ChainingMetadataProvider;
33 import org.opensaml.saml2.metadata.provider.MetadataFilter;
34 import org.opensaml.saml2.metadata.provider.MetadataProvider;
35 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
36 import org.opensaml.security.TrustEngine;
37 import org.opensaml.security.X509EntityCredential;
38 import org.opensaml.xml.XMLObject;
39 import org.w3c.dom.Element;
41 import edu.internet2.middleware.shibboleth.aa.AAAttribute;
42 import edu.internet2.middleware.shibboleth.aa.AAException;
43 import edu.internet2.middleware.shibboleth.aa.arp.ArpEngine;
44 import edu.internet2.middleware.shibboleth.aa.arp.ArpProcessingException;
45 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
46 import edu.internet2.middleware.shibboleth.artifact.ArtifactMapper;
47 import edu.internet2.middleware.shibboleth.common.Credential;
48 import edu.internet2.middleware.shibboleth.common.NameMapper;
49 import edu.internet2.middleware.shibboleth.common.RelyingParty;
50 import edu.internet2.middleware.shibboleth.common.ServiceProviderMapper;
51 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
52 import edu.internet2.middleware.shibboleth.common.provider.ShibbolethTrustEngine;
53 import edu.internet2.middleware.shibboleth.metadata.MetadataProviderFactory;
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 MetadataProvider {
63 private static Logger log = Logger.getLogger(IdPProtocolSupport.class.getName());
64 private Logger transactionLog;
65 private IdPConfig config;
66 private NameMapper nameMapper;
67 private ServiceProviderMapper spMapper;
68 private ArpEngine arpEngine;
69 private AttributeResolver resolver;
70 private ArtifactMapper artifactMapper;
71 private Semaphore throttle;
72 private TrustEngine<X509EntityCredential> trust = new ShibbolethTrustEngine();
73 private ChainingMetadataProvider wrappedMetadataProvider = new ChainingMetadataProvider();
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 wrappedMetadataProvider.addMetadataProvider(MetadataProviderFactory.loadProvider(element));
177 } catch (MetadataProviderException e) {
178 log.error("Unable to load Metadata Provider. Skipping...");
183 public Collection<? extends SAMLAttribute> getReleaseAttributes(Principal principal, RelyingParty relyingParty,
184 String requester) throws AAException {
187 Collection<URI> potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester);
188 return getReleaseAttributes(principal, relyingParty, requester, potentialAttributes);
190 } catch (ArpProcessingException e) {
191 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
193 throw new AAException("Error retrieving data for principal.");
197 public Collection<? extends SAMLAttribute> getReleaseAttributes(Principal principal, RelyingParty relyingParty,
198 String requester, Collection<URI> attributeNames) throws AAException {
201 Map<String, AAAttribute> attributes = new HashMap<String, AAAttribute>();
202 for (URI name : attributeNames) {
204 AAAttribute attribute = null;
205 if (relyingParty.wantsSchemaHack()) {
206 attribute = new AAAttribute(name.toString(), true);
208 attribute = new AAAttribute(name.toString(), false);
210 attributes.put(attribute.getName(), attribute);
213 Collection<URI> constraintAttributes = arpEngine.listRequiredConstraintAttributes(principal, requester,
215 for (URI name : constraintAttributes) {
216 if (!attributes.containsKey(name.toString())) {
217 // don't care about schema hack since these attributes won't be returned to SP
218 AAAttribute attribute = new AAAttribute(name.toString(), false);
219 attributes.put(attribute.getName(), attribute);
223 return resolveAttributes(principal, requester, relyingParty.getIdentityProvider().getProviderId(),
226 } catch (SAMLException e) {
227 log.error("An error occurred while creating attributes for principal (" + principal.getName() + ") :"
229 throw new AAException("Error retrieving data for principal.");
231 } catch (ArpProcessingException e) {
232 log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
234 throw new AAException("Error retrieving data for principal.");
238 public Collection<? extends SAMLAttribute> resolveAttributes(Principal principal, String requester,
239 String responder, Map<String, AAAttribute> attributeSet) throws ArpProcessingException {
241 resolver.resolveAttributes(principal, requester, responder, attributeSet);
242 arpEngine.filterAttributes(attributeSet.values(), principal, requester);
243 return attributeSet.values();
246 public Collection<? extends SAMLAttribute> resolveAttributesNoPolicies(Principal principal, String requester,
247 String responder, Map<String, AAAttribute> attributeSet) {
249 resolver.resolveAttributes(principal, requester, responder, attributeSet);
250 return attributeSet.values();
254 * Cleanup resources that won't be released when this object is garbage-collected
256 public void destroy() {
262 public ArtifactMapper getArtifactMapper() {
264 return artifactMapper;
267 public TrustEngine<X509EntityCredential> getTrust() {
272 public boolean requireValidMetadata() {
274 return wrappedMetadataProvider.requireValidMetadata();
277 public void setRequireValidMetadata(boolean requireValidMetadata) {
279 wrappedMetadataProvider.setRequireValidMetadata(requireValidMetadata);
282 public MetadataFilter getMetadataFilter() {
284 return wrappedMetadataProvider.getMetadataFilter();
287 public void setMetadataFilter(MetadataFilter newFilter) throws MetadataProviderException {
289 wrappedMetadataProvider.setMetadataFilter(newFilter);
292 public XMLObject getMetadata() throws MetadataProviderException {
294 return wrappedMetadataProvider.getMetadata();
297 public EntitiesDescriptor getEntitiesDescriptor(String name) throws MetadataProviderException {
299 return wrappedMetadataProvider.getEntitiesDescriptor(name);
302 public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
304 return wrappedMetadataProvider.getEntityDescriptor(entityID);
307 public List<RoleDescriptor> getRole(String entityID, QName roleName) throws MetadataProviderException {
309 return wrappedMetadataProvider.getRole(entityID, roleName);
312 public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol)
313 throws MetadataProviderException {
315 return wrappedMetadataProvider.getRole(entityID, roleName, supportedProtocol);
318 public int providerCount() {
320 return wrappedMetadataProvider.getProviders().size();
323 private class Semaphore {
327 public Semaphore(int value) {
332 public synchronized void enter() {
338 } catch (InterruptedException e) {
339 // squelch and continue
344 public synchronized void exit() {