Moved java src to apache license.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / ShibBinding.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 /*
18  * ShibBinding.java
19  * 
20  * Corresponds to ShibBinding.cpp
21  * 
22  * A Shibboleth wrapper around the services of SAMLSOAPBinding,
23  * this class adds processing from the Shibboleth configuration 
24  * to the process of sending a SAMLRequest and getting a SAMLResponse.
25  * In particular, the caller of a ShibBinding provides arguments
26  * that identify the target of the request from the Metadata, and
27  * the caller passes an implementation of Trust so that signatures
28  * can be validated.
29  */
30 package edu.internet2.middleware.shibboleth.serviceprovider;
31
32 import java.util.Iterator;
33
34 import org.apache.log4j.Logger;
35 import org.opensaml.NoSuchProviderException;
36 import org.opensaml.SAMLAssertion;
37 import org.opensaml.SAMLAuthorityBinding;
38 import org.opensaml.SAMLBinding;
39 import org.opensaml.SAMLBindingFactory;
40 import org.opensaml.SAMLException;
41 import org.opensaml.SAMLRequest;
42 import org.opensaml.SAMLResponse;
43 import org.opensaml.SAMLSOAPHTTPBinding;
44 import org.opensaml.TrustException;
45 import org.opensaml.provider.SOAPHTTPBindingProvider;
46
47 import edu.internet2.middleware.shibboleth.common.Trust;
48 import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityDescriptor;
49 import edu.internet2.middleware.shibboleth.metadata.Endpoint;
50 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
51
52 /**
53  * Wrapper for a SAMLBinding send/receive operation.
54  * 
55  * <p>A ServiceProvider creates a ShibBinding object and then calls
56  * its send() method. The logic is certainly capable of generating any
57  * SAML Request/Response sequence. However, the variables have been
58  * declared to have more specific types than the general logic, so this
59  * version can only be used by a Service Provider to make an attribute query
60  * to the AA.</p>
61  * 
62  * <p>The ShibBinding doesn't hold any important resources. The
63  * identity of the AA isn't passed until the send() method and could change
64  * across calls, so there aren't any persistent network resources. Nothing
65  * prevents a ShibBinding object from being reused, but normally it is
66  * just a transient object as in resp=(new ShibBinding(appid)).send(req,...)</p>
67  * 
68  * @author Howard Gilbert
69  */
70 public class ShibBinding {
71         
72         private static Logger log = Logger.getLogger(ShibBinding.class);
73         
74         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
75         
76         private String applicationId = null;
77         
78         /**
79          * While the C++ constructor takes iterators over the Trust and 
80          * Metadata, here we provide the key of an ApplicationInfo object
81          * that contains them.
82          * 
83          * @param applicationId
84          * @throws NoSuchProviderException
85          */
86         public 
87         ShibBinding(
88                         String applicationId) throws NoSuchProviderException {
89                 this.applicationId=applicationId;
90         }
91
92         /**
93          * Send a SAMLRequest and get back a SAMLResponse.
94          * 
95          * <p>Although this logic could be generalized, this version
96          * declares the arguments to be of specific types (an AA role)
97          * so it can only be used to send the Attribute Query and get back
98          * the Attribute Assertions.
99          * 
100          * @param req        SAMLRequest to send
101          * @param role       AttributeAuthorityRole representing destination
102          * @param audiences  Audience strings to check SAML conditions
103          * @param bindings   Stupid idea. Don't use this parameter
104          * @return           The SAMLResponse
105          * @throws SAMLException
106          */
107         public 
108                         SAMLResponse 
109         send (
110                         SAMLRequest req,
111                         AttributeAuthorityDescriptor role,
112                         String[] audiences,
113                         SAMLAuthorityBinding[] bindings,
114             Trust trust) 
115         throws SAMLException {
116                 
117                 // For the duration of the request, get local references to
118                 // configuration objects that might change between requests
119                 ServiceProviderConfig config = context.getServiceProviderConfig();
120                 ApplicationInfo appinfo = config.getApplication(applicationId);
121                 
122         SAMLBinding sbinding = null;
123                 SAMLResponse resp = null;
124                 String prevBinding = null;
125         
126                 /*
127                  * Try any inline bindings provided by 1.0/1.1 IdPs. 
128                  */
129                 if (bindings!=null) {
130                         for (int ibinding=0;ibinding<bindings.length;ibinding++) {
131                                 try {
132                                         SAMLAuthorityBinding binding = bindings[ibinding];
133                                         if (!binding.getBinding().equals(prevBinding)) {
134                                                 prevBinding = binding.getBinding();
135                         sbinding = SAMLBindingFactory.getInstance(binding.getBinding());
136                     }
137                                         resp=sbinding.send(binding.getLocation(),req);
138                                         validateResponseSignatures(role, appinfo, resp);
139                                         return resp;
140                 } catch (TrustException e) {
141                     log.error("Unable to validate signatures on attribute response: " + e);
142                     continue;
143                 } catch (SAMLException e) {
144                     log.error("Unable to query attributes: " + e);
145                     continue;
146                 }
147                         }
148                 }
149                 
150                 /*
151                  * Try each metadata endpoint...
152                  */
153                 Iterator ends = role.getAttributeServiceManager().getEndpoints();
154         while (ends.hasNext()) {
155             Endpoint endpoint = (Endpoint)ends.next();
156             try {
157                 if (!endpoint.getBinding().equals(prevBinding)) {
158                     prevBinding = endpoint.getBinding();
159                     sbinding = SAMLBindingFactory.getInstance(endpoint.getBinding());
160                 }
161                 if (sbinding instanceof SAMLSOAPHTTPBinding) {
162                     SAMLSOAPHTTPBinding httpbind = (SAMLSOAPHTTPBinding)sbinding;
163                     httpbind.addHook(new ShibHttpHook(role,trust));
164                 }
165                 resp=sbinding.send(endpoint.getLocation(),req);
166                 validateResponseSignatures(role, appinfo, resp);
167                 return resp;
168             } catch (TrustException e) {
169                 log.error("Unable to validate signatures on attribute response: " + e);
170                 continue;
171             } catch (SAMLException e) {
172                 log.error("Unable to query attributes: " + e);
173                 continue;
174             }
175         }
176         return null;
177         }
178
179         /**
180          * Validate signatures in response against the Trust configuration.
181          * 
182          * @param role     OriginSite
183          * @param appinfo  Application data
184          * @param resp     SAML response
185          * @throws TrustException on failure
186          */
187         private void 
188         validateResponseSignatures(
189                         AttributeAuthorityDescriptor role, 
190                         ApplicationInfo appinfo, 
191                         SAMLResponse resp) 
192         throws TrustException {
193                 
194                 if (resp.isSigned()&& !appinfo.validate(resp,role)) {
195                         throw new TrustException("Unable to validate signature of response");
196                 }
197                 
198                 Iterator assertions = resp.getAssertions();
199                 while (assertions.hasNext()) {
200                         SAMLAssertion assertion = (SAMLAssertion) assertions.next();
201                         
202                         // TODO Dropped some logic validating conditions
203                         
204                         if (assertion.isSigned() && 
205                                 !appinfo.validate(assertion,role)) {
206                                 throw new TrustException("Unable to validate signature of assertion in response");
207                         }
208                 }
209         }
210 }