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