Added some rarely used methods to sync metadata API with C++
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / IdPProtocolSupport.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp;
18
19 import java.net.URI;
20 import java.net.URL;
21 import java.security.Principal;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Iterator;
25
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;
35
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;
54
55 /**
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.
58  * 
59  * @author Walter Hoehn
60  */
61 public class IdPProtocolSupport implements Metadata {
62
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();
74
75         IdPProtocolSupport(IdPConfig config, Logger transactionLog, NameMapper nameMapper, ServiceProviderMapper spMapper,
76                         ArpEngine arpEngine, AttributeResolver resolver, ArtifactMapper artifactMapper)
77                         throws ShibbolethConfigurationException {
78
79                 this.transactionLog = transactionLog;
80                 this.config = config;
81                 this.nameMapper = nameMapper;
82                 this.spMapper = spMapper;
83                 spMapper.setMetadata(this);
84                 this.arpEngine = arpEngine;
85                 this.resolver = resolver;
86                 this.artifactMapper = artifactMapper;
87
88                 // Load a semaphore that throttles how many requests the IdP will handle at once
89                 throttle = new Semaphore(config.getMaxThreads());
90         }
91
92         public Logger getTransactionLog() {
93
94                 return transactionLog;
95         }
96
97         public IdPConfig getIdPConfig() {
98
99                 return config;
100         }
101
102         public NameMapper getNameMapper() {
103
104                 return nameMapper;
105         }
106
107         public ServiceProviderMapper getServiceProviderMapper() {
108
109                 return spMapper;
110         }
111
112         public void signAssertions(SAMLAssertion[] assertions, RelyingParty relyingParty) throws InvalidCryptoException,
113                         SAMLException {
114
115                 if (relyingParty.getIdentityProvider().getSigningCredential() == null
116                                 || relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey() == null) { throw new InvalidCryptoException(
117                                 SAMLException.RESPONDER, "Invalid signing credential."); }
118
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;
125                         } else {
126                                 throw new InvalidCryptoException(SAMLException.RESPONDER,
127                                                 "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
128                         }
129
130                         try {
131                                 throttle.enter();
132                                 assertions[i].sign(assertionAlgorithm, relyingParty.getIdentityProvider().getSigningCredential()
133                                                 .getPrivateKey(), Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential()
134                                                 .getX509CertificateChain()));
135                         } finally {
136                                 throttle.exit();
137                         }
138                 }
139         }
140
141         public void signResponse(SAMLResponse response, RelyingParty relyingParty) throws SAMLException {
142
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."); }
147
148                 // Sign the response
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;
154                 } else {
155                         throw new InvalidCryptoException(SAMLException.RESPONDER,
156                                         "The Shibboleth IdP currently only supports signing with RSA and DSA keys.");
157                 }
158                 try {
159                         throttle.enter();
160                         response.sign(responseAlgorithm, relyingParty.getIdentityProvider().getSigningCredential().getPrivateKey(),
161                                         Arrays.asList(relyingParty.getIdentityProvider().getSigningCredential().getX509CertificateChain()));
162                 } finally {
163                         throttle.exit();
164                 }
165         }
166
167         protected void addMetadataProvider(Element element) {
168
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.");
172                         return;
173                 }
174
175                 try {
176                         metadata.add(MetadataProviderFactory.loadProvider(element));
177                 } catch (MetadataException e) {
178                         log.error("Unable to load Metadata Provider.  Skipping...");
179                 }
180         }
181
182         public int providerCount() {
183
184                 return metadata.size();
185         }
186
187         public EntityDescriptor lookup(String providerId, boolean strict) {
188
189                 Iterator iterator = metadata.iterator();
190                 while (iterator.hasNext()) {
191                         EntityDescriptor provider = ((Metadata) iterator.next()).lookup(providerId);
192                         if (provider != null) { return provider; }
193                 }
194                 return null;
195         }
196
197         public EntityDescriptor lookup(Artifact artifact, boolean strict) {
198
199                 Iterator iterator = metadata.iterator();
200                 while (iterator.hasNext()) {
201                         EntityDescriptor provider = ((Metadata) iterator.next()).lookup(artifact);
202                         if (provider != null) { return provider; }
203                 }
204                 return null;
205         }
206
207         public EntityDescriptor lookup(String id) {
208                 return lookup(id,true);
209         }
210
211         public EntityDescriptor lookup(Artifact artifact) {
212                 return lookup(artifact,true);
213         }
214
215         public EntityDescriptor getRootEntity() {
216                 return null;
217         }
218
219         public EntitiesDescriptor getRootEntities() {
220                 return null;
221         }
222         
223         public SAMLAttribute[] getReleaseAttributes(Principal principal, RelyingParty relyingParty, String requester,
224                         URL resource) throws AAException {
225
226                 try {
227                         URI[] potentialAttributes = arpEngine.listPossibleReleaseAttributes(principal, requester, resource);
228                         return getReleaseAttributes(principal, relyingParty, requester, resource, potentialAttributes);
229
230                 } catch (ArpProcessingException e) {
231                         log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
232                                         + e.getMessage());
233                         throw new AAException("Error retrieving data for principal.");
234                 }
235         }
236
237         public SAMLAttribute[] getReleaseAttributes(Principal principal, RelyingParty relyingParty, String requester,
238                         URL resource, URI[] attributeNames) throws AAException {
239
240                 try {
241                         AAAttributeSet attributeSet = new AAAttributeSet();
242                         for (int i = 0; i < attributeNames.length; i++) {
243
244                                 AAAttribute attribute = null;
245                                 if (relyingParty.wantsSchemaHack()) {
246                                         attribute = new AAAttribute(attributeNames[i].toString(), true);
247                                 } else {
248                                         attribute = new AAAttribute(attributeNames[i].toString(), false);
249                                 }
250
251                                 attributeSet.add(attribute);
252                         }
253
254                         return resolveAttributes(principal, requester, relyingParty.getIdentityProvider().getProviderId(),
255                                         resource, attributeSet);
256
257                 } catch (SAMLException e) {
258                         log.error("An error occurred while creating attributes for principal (" + principal.getName() + ") :"
259                                         + e.getMessage());
260                         throw new AAException("Error retrieving data for principal.");
261
262                 } catch (ArpProcessingException e) {
263                         log.error("An error occurred while processing the ARPs for principal (" + principal.getName() + ") :"
264                                         + e.getMessage());
265                         throw new AAException("Error retrieving data for principal.");
266                 }
267         }
268
269         private SAMLAttribute[] resolveAttributes(Principal principal, String requester, String responder, URL resource,
270                         AAAttributeSet attributeSet) throws ArpProcessingException {
271
272                 resolver.resolveAttributes(principal, requester, responder, attributeSet);
273                 arpEngine.filterAttributes(attributeSet, principal, requester, resource);
274                 return attributeSet.getAttributes();
275         }
276
277         /**
278          * Cleanup resources that won't be released when this object is garbage-collected
279          */
280         public void destroy() {
281
282                 resolver.destroy();
283                 arpEngine.destroy();
284         }
285
286         public ArtifactMapper getArtifactMapper() {
287
288                 return artifactMapper;
289         }
290
291         public Trust getTrust() {
292
293                 return trust;
294         }
295
296         private class Semaphore {
297
298                 private int value;
299
300                 public Semaphore(int value) {
301
302                         this.value = value;
303                 }
304
305                 public synchronized void enter() {
306
307                         --value;
308                         if (value < 0) {
309                                 try {
310                                         wait();
311                                 } catch (InterruptedException e) {
312                                         // squelch and continue
313                                 }
314                         }
315                 }
316
317                 public synchronized void exit() {
318
319                         ++value;
320                         notify();
321                 }
322         }
323 }