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