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