Bunch o' bug fixes, all IdP configuration files now load.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractAttributeQuery.java
1 /*
2  * Copyright [2006] [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.profile.saml2;
18
19 import javax.servlet.ServletRequest;
20 import javax.servlet.ServletResponse;
21
22 import org.apache.log4j.Logger;
23 import org.joda.time.DateTime;
24 import org.opensaml.common.binding.BindingException;
25 import org.opensaml.common.binding.decoding.MessageDecoder;
26 import org.opensaml.common.binding.encoding.MessageEncoder;
27 import org.opensaml.log.Level;
28 import org.opensaml.saml2.core.Assertion;
29 import org.opensaml.saml2.core.AttributeQuery;
30 import org.opensaml.saml2.core.AttributeStatement;
31 import org.opensaml.saml2.core.Response;
32 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
33
34 import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
35 import edu.internet2.middleware.shibboleth.common.attribute.SAML2AttributeAuthority;
36 import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethAttributeRequestContext;
37 import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
38 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
39 import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
40 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
41 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
42 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AttributeQueryConfiguration;
43 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
44 import edu.internet2.middleware.shibboleth.idp.session.Session;
45
46 /**
47  * SAML 2.0 Attribute Query profile handler.
48  */
49 public abstract class AbstractAttributeQuery extends AbstractSAML2ProfileHandler {
50
51     /** Class logger. */
52     private static Logger log = Logger.getLogger(AbstractAttributeQuery.class);
53
54     /** {@inheritDoc} */
55     public String getProfileId() {
56         return "urn:oasis:names:tc:SAML:2.0:profiles:query";
57     }
58
59     /** {@inheritDoc} */
60     public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
61             throws ProfileException {
62
63         AttributeQueryRequestContext requestContext = new AttributeQueryRequestContext(request, response);
64
65         getMessageDecoder(requestContext);
66         
67         decodeRequest(requestContext);
68
69         buildResponse(requestContext);
70
71         getMessageEncoder(requestContext);
72
73         try {
74             requestContext.getMessageEncoder().encode();
75             writeAuditLogEntry(requestContext);
76         } catch (BindingException e) {
77             log.error("Unable to encode response the relying party: " + requestContext.getRelyingPartyId(), e);
78             throw new ProfileException("Unable to encode response the relying party: "
79                     + requestContext.getRelyingPartyId(), e);
80         }
81     }
82
83     /**
84      * Gets a populated message decoder.
85      * 
86      * @param requestContext current request context
87      * 
88      * @throws ProfileException thrown if there is no message decoder that may be used to decoder the incoming request
89      */
90     protected abstract void getMessageDecoder(AttributeQueryRequestContext requestContext) throws ProfileException;
91
92     /**
93      * Gets a populated message encoder.
94      * 
95      * @param requestContext current request context
96      * 
97      * @throws ProfileException thrown if there is no message encoder that may be used to encoder the outgoing response
98      */
99     protected abstract void getMessageEncoder(AttributeQueryRequestContext requestContext) throws ProfileException;
100
101     /**
102      * Decodes the message in the request and adds it to the request context.
103      * 
104      * @param requestContext request context contianing the request to decode
105      * 
106      * @throws ProfileException throw if there is a problem decoding the request
107      */
108     protected void decodeRequest(AttributeQueryRequestContext requestContext)
109             throws ProfileException {
110
111         try {
112             requestContext.getMessageDecoder().decode();
113             if (log.isDebugEnabled()) {
114                 log.debug("decoded http servlet request");
115             }
116             requestContext.setAttributeQuery((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
117         } catch (BindingException e) {
118             log.error("Error decoding attribute query message", e);
119             throw new ProfileException("Error decoding attribute query message");
120         }
121     }
122
123     /**
124      * Builds a response to the attribute query within the request context.
125      * 
126      * @param requestContext current request context
127      * 
128      * @throws ProfileException thrown if there is a problem creating the SAML response
129      */
130     protected void buildResponse(AttributeQueryRequestContext requestContext) throws ProfileException {
131         DateTime issueInstant = new DateTime();
132
133         // create the attribute statement
134         AttributeStatement attributeStatement = buildAttributeStatement(requestContext);
135
136         // create the assertion and add the attribute statement
137         Assertion assertion = buildAssertion(issueInstant, requestContext.getRelyingPartyConfiguration(),
138                 requestContext.getProfileConfiguration());
139         assertion.getAttributeStatements().add(attributeStatement);
140
141         // create the SAML response and add the assertion
142         Response samlResponse = getResponseBuilder().buildObject();
143         populateStatusResponse(samlResponse, issueInstant, requestContext.getAttributeQuery(), requestContext
144                 .getRelyingPartyConfiguration());
145         
146         // TODO handle subject
147         samlResponse.getAssertions().add(assertion);
148
149         // sign the assertion if it should be signed
150         signAssertion(assertion, requestContext.getRelyingPartyConfiguration(), requestContext
151                 .getProfileConfiguration());
152
153         requestContext.setAttributeQueryResponse(samlResponse);
154     }
155
156     /**
157      * Executes a query for attributes and builds a SAML attribute statement from the results.
158      * 
159      * @param requestContext current request context
160      * 
161      * @return attribute statement resulting from the query
162      * 
163      * @throws ProfileException thrown if there is a problem making the query
164      */
165     protected AttributeStatement buildAttributeStatement(AttributeQueryRequestContext requestContext)
166             throws ProfileException {
167         ShibbolethAttributeRequestContext attributeRequestContext = buildAttributeRequestContext(requestContext
168                 .getRelyingPartyId(), requestContext.getUserSession(), requestContext.getProfileRequest());
169
170         try {
171             SAML2AttributeAuthority attributeAuthority = requestContext.getProfileConfiguration()
172                     .getAttributeAuthority();
173             return attributeAuthority.performAttributeQuery(attributeRequestContext);
174         } catch (AttributeRequestException e) {
175             log.error("Error resolving attributes", e);
176             throw new ProfileException("Error resolving attributes", e);
177         }
178     }
179
180     /**
181      * Builds an attribute request context for this request.
182      * 
183      * @param spEntityId entity ID of the service provider
184      * @param userSession current user's session
185      * @param request current request
186      * 
187      * @return the attribute request context
188      * 
189      * @throws ProfileException thrown if the metadata information can not be located for the given service provider
190      */
191     protected ShibbolethAttributeRequestContext buildAttributeRequestContext(String spEntityId, Session userSession,
192             ProfileRequest<ServletRequest> request) throws ProfileException {
193         ServiceInformation spInformation = userSession.getServiceInformation(spEntityId);
194         ShibbolethAttributeRequestContext requestContext = null;
195         try {
196             requestContext = new ShibbolethAttributeRequestContext(getMetadataProvider(),
197                     getRelyingPartyConfiguration(spEntityId));
198             requestContext.setPrincipalName(userSession.getPrincipalID());
199             requestContext.setPrincipalAuthenticationMethod(spInformation.getAuthenticationMethod()
200                     .getAuthenticationMethod());
201             requestContext.setRequest(request.getRawRequest());
202             return requestContext;
203         } catch (MetadataProviderException e) {
204             log.error("Error creating ShibbolethAttributeRequestContext", e);
205             throw new ProfileException("Error retrieving metadata", e);
206         }
207     }
208
209     /**
210      * Writes an aduit log entry indicating the successful response to the attribute request.
211      * 
212      * @param requestContext current request context
213      */
214     protected void writeAuditLogEntry(AttributeQueryRequestContext requestContext) {
215         AuditLogEntry auditLogEntry = new AuditLogEntry();
216         auditLogEntry.setMessageProfile(getProfileId());
217         auditLogEntry.setPrincipalAuthenticationMethod(requestContext.getUserSession().getServiceInformation(
218                 requestContext.getRelyingPartyId()).getAuthenticationMethod().getAuthenticationMethod());
219         auditLogEntry.setPrincipalId(requestContext.getUserSession().getPrincipalID());
220         auditLogEntry.setProviderId(requestContext.getRelyingPartyConfiguration().getProviderId());
221         auditLogEntry.setRelyingPartyId(requestContext.getRelyingPartyId());
222         auditLogEntry.setRequestBinding(requestContext.getMessageDecoder().getBindingURI());
223         auditLogEntry.setRequestId(requestContext.getAttributeQuery().getID());
224         auditLogEntry.setResponseBinding(requestContext.getMessageEncoder().getBindingURI());
225         auditLogEntry.setResponseId(requestContext.getAttributeQueryResponse().getID());
226         getAduitLog().log(Level.CRITICAL, auditLogEntry);
227     }
228
229     /** Basic data structure used to accumulate information as a request is being processed. */
230     protected class AttributeQueryRequestContext {
231
232         /** Current user's session. */
233         private Session userSession;
234
235         /** Current profile request. */
236         private ProfileRequest<ServletRequest> profileRequest;
237
238         /** Decoder used to decode the incoming request. */
239         private MessageDecoder<ServletRequest> messageDecoder;
240
241         /** Current profile response. */
242         private ProfileResponse<ServletResponse> profileResponse;
243
244         /** Encoder used to encode the outgoing response. */
245         private MessageEncoder<ServletResponse> messageEncoder;
246
247         /** Attribute query made by the relying party. */
248         private AttributeQuery attributeQuery;
249
250         /** Attribute query response to the relying party. */
251         private Response attributeQueryResponse;
252
253         /** ID of the relying party. */
254         private String relyingPartyId;
255
256         /** Relying party configuration information. */
257         private RelyingPartyConfiguration relyingPartyConfiguration;
258
259         /** Attribute query profile configuration for the relying party. */
260         private AttributeQueryConfiguration profileConfiguration;
261
262         /**
263          * Constructor.
264          * 
265          * @param request current profile request
266          * @param response current profile response
267          */
268         public AttributeQueryRequestContext(ProfileRequest<ServletRequest> request,
269                 ProfileResponse<ServletResponse> response) {
270             userSession = getSessionManager().getSession(getUserSessionId(request));
271             profileRequest = request;
272             profileResponse = response;
273
274         }
275
276         /**
277          * Gets the attribute query from the relying party.
278          * 
279          * @return attribute query from the relying party
280          */
281         public AttributeQuery getAttributeQuery() {
282             return attributeQuery;
283         }
284
285         /**
286          * Sets the attribute query from the relying party. This also populates the relying party ID, configuration, and
287          * profile configuration using information from the query.
288          * 
289          * @param query attribute query from the relying party
290          */
291         public void setAttributeQuery(AttributeQuery query) {
292             attributeQuery = query;
293             relyingPartyId = attributeQuery.getIssuer().getValue();
294             relyingPartyConfiguration = getRelyingPartyConfigurationManager().getRelyingPartyConfiguration(
295                     relyingPartyId);
296             profileConfiguration = (AttributeQueryConfiguration) relyingPartyConfiguration
297                     .getProfileConfiguration(AttributeQueryConfiguration.PROFILE_ID);
298         }
299
300         /**
301          * Gets the attribute query response.
302          * 
303          * @return attribute query response
304          */
305         public Response getAttributeQueryResponse() {
306             return attributeQueryResponse;
307         }
308
309         /**
310          * Sets the attribute query response.
311          * 
312          * @param response attribute query response
313          */
314         public void setAttributeQueryResponse(Response response) {
315             attributeQueryResponse = response;
316         }
317
318         /**
319          * Gets the decoder used to decode the request.
320          * 
321          * @return decoder used to decode the request
322          */
323         public MessageDecoder<ServletRequest> getMessageDecoder() {
324             return messageDecoder;
325         }
326
327         /**
328          * Sets the decoder used to decode the request.
329          * 
330          * @param decoder decoder used to decode the request
331          */
332         public void setMessageDecoder(MessageDecoder<ServletRequest> decoder) {
333             messageDecoder = decoder;
334         }
335
336         /**
337          * Gets the encoder used to encoder the response.
338          * 
339          * @return encoder used to encoder the response
340          */
341         public MessageEncoder<ServletResponse> getMessageEncoder() {
342             return messageEncoder;
343         }
344
345         /**
346          * Sets the encoder used to encoder the response.
347          * 
348          * @param encoder encoder used to encoder the response
349          */
350         public void setMessageEncoder(MessageEncoder<ServletResponse> encoder) {
351             messageEncoder = encoder;
352         }
353
354         /**
355          * Gets the attribute profile configuration for the relying party.
356          * 
357          * @return attribute profile configuration for the relying party
358          */
359         public AttributeQueryConfiguration getProfileConfiguration() {
360             return profileConfiguration;
361         }
362
363         /**
364          * Gets the current profile request.
365          * 
366          * @return current profile request
367          */
368         public ProfileRequest<ServletRequest> getProfileRequest() {
369             return profileRequest;
370         }
371
372         /**
373          * Gets the current profile response.
374          * 
375          * @return current profile response
376          */
377         public ProfileResponse<ServletResponse> getProfileResponse() {
378             return profileResponse;
379         }
380
381         /**
382          * Gets the configuration information specific to the relying party that made the attribute query.
383          * 
384          * @return configuration information specific to the relying party that made the attribute query
385          */
386         public RelyingPartyConfiguration getRelyingPartyConfiguration() {
387             return relyingPartyConfiguration;
388         }
389
390         /**
391          * Gets the ID of the relying party.
392          * 
393          * @return ID of the relying party
394          */
395         public String getRelyingPartyId() {
396             return relyingPartyId;
397         }
398
399         /**
400          * Gets the current user's session.
401          * 
402          * @return current user's session
403          */
404         public Session getUserSession() {
405             return userSession;
406         }
407     }
408 }