Mostly synched with OpenSAML changes
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml1 / AttributeQueryProfileHandler.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.saml1;
18
19 import java.util.ArrayList;
20
21 import org.apache.log4j.Logger;
22 import org.opensaml.common.xml.SAMLConstants;
23 import org.opensaml.saml1.core.AttributeQuery;
24 import org.opensaml.saml1.core.Request;
25 import org.opensaml.saml1.core.Response;
26 import org.opensaml.saml1.core.Statement;
27 import org.opensaml.saml1.core.StatusCode;
28 import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
29 import org.opensaml.saml2.metadata.SPSSODescriptor;
30 import org.opensaml.saml2.metadata.provider.MetadataProvider;
31 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
32 import org.opensaml.ws.message.decoder.MessageDecodingException;
33 import org.opensaml.ws.message.encoder.MessageEncodingException;
34 import org.opensaml.ws.security.SecurityPolicyException;
35 import org.opensaml.ws.transport.http.HTTPInTransport;
36 import org.opensaml.ws.transport.http.HTTPOutTransport;
37
38 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
39 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
40 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AttributeQueryConfiguration;
41
42 /**
43  * SAML 1 Attribute Query profile handler.
44  */
45 public class AttributeQueryProfileHandler extends AbstractSAML1ProfileHandler {
46
47     /** Class logger. */
48     private final Logger log = Logger.getLogger(AttributeQueryProfileHandler.class);
49
50     /** {@inheritDoc} */
51     public String getProfileId() {
52         return "urn:mace:shibboleth:2.0:idp:profiles:saml1:query:attribute";
53     }
54
55     /** {@inheritDoc} */
56     public void processRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport) throws ProfileException {
57         AttributeQueryContext requestContext = decodeRequest(inTransport, outTransport);
58
59         Response samlResponse;
60         try {
61             if (requestContext.getRelyingPartyConfiguration() == null) {
62                 log.error("SAML 1 Attribute Query profile is not configured for relying party "
63                         + requestContext.getRelyingPartyEntityId());
64                 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, StatusCode.REQUEST_DENIED,
65                         "SAML 1 Attribute Query profile is not configured for relying party "
66                                 + requestContext.getRelyingPartyEntityId()));
67                 samlResponse = buildErrorResponse(requestContext);
68             }
69
70             resolvePrincipal(requestContext);
71             resolveAttributes(requestContext);
72
73             ArrayList<Statement> statements = new ArrayList<Statement>();
74             statements.add(buildAttributeStatement(requestContext, "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches"));
75
76             samlResponse = buildResponse(requestContext, statements);
77         } catch (ProfileException e) {
78             samlResponse = buildErrorResponse(requestContext);
79         }
80
81         requestContext.setOutboundSAMLMessage(samlResponse);
82         requestContext.setOutboundSAMLMessageId(samlResponse.getID());
83         requestContext.setOutboundSAMLMessageIssueInstant(samlResponse.getIssueInstant());
84         encodeResponse(requestContext);
85         writeAuditLogEntry(requestContext);
86     }
87     
88     /**
89      * Decodes an incoming request and populates a created request context with the resultant information.
90      * 
91      * @param inTransport inbound message transport
92      * @param outTransport outbound message transport
93      * 
94      * @return the created request context
95      * 
96      * @throws ProfileException throw if there is a problem decoding the request
97      */
98     protected AttributeQueryContext decodeRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
99             throws ProfileException {
100         if (log.isDebugEnabled()) {
101             log.debug("Decoding incomming request");
102         }
103
104         MetadataProvider metadataProvider = getMetadataProvider();
105
106         AttributeQueryContext requestContext = new AttributeQueryContext();
107         requestContext.setMessageInTransport(inTransport);
108         requestContext.setInboundSAMLProtocol(SAMLConstants.SAML11P_NS);
109         requestContext.setMessageOutTransport(outTransport);
110         requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML11P_NS);
111         requestContext.setMetadataProvider(metadataProvider);
112
113         try {
114             getMessageDecoder().decode(requestContext);
115             if (log.isDebugEnabled()) {
116                 log.debug("Decoded request");
117             }
118             return requestContext;
119         } catch (MessageDecodingException e) {
120             log.error("Error decoding attribute query message", e);
121             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null, "Error decoding message"));
122             throw new ProfileException("Error decoding attribute query message");
123         } catch (SecurityPolicyException e) {
124             log.error("Message did not meet security policy requirements", e);
125             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, StatusCode.REQUEST_DENIED,
126                     "Message did not meet security policy requirements"));
127             throw new ProfileException("Message did not meet security policy requirements", e);
128         } finally {
129             // Set as much information as can be retrieved from the decoded message
130             try {
131                 Request attributeRequest = requestContext.getInboundSAMLMessage();
132                 requestContext.setInboundSAMLMessageId(attributeRequest.getID());
133                 requestContext.setInboundSAMLMessageIssueInstant(attributeRequest.getIssueInstant());
134
135                 String relyingPartyId = requestContext.getRelyingPartyEntityId();
136                 requestContext.setRelyingPartyMetadata(metadataProvider.getEntityDescriptor(relyingPartyId));
137                 requestContext.setRelyingPartyRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
138                 requestContext.setRelyingPartyRoleMetadata(requestContext.getRelyingPartyMetadata().getSPSSODescriptor(
139                         SAMLConstants.SAML10P_NS));
140                 RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
141                 requestContext.setRelyingPartyConfiguration(rpConfig);
142
143                 String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
144                 requestContext.setAssertingPartyEntityId(assertingPartyId);
145                 requestContext.setAssertingPartyMetadata(metadataProvider.getEntityDescriptor(assertingPartyId));
146                 requestContext.setAssertingPartyRole(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_NAME);
147                 requestContext.setAssertingPartyRoleMetadata(requestContext.getAssertingPartyMetadata()
148                         .getAttributeAuthorityDescriptor(SAMLConstants.SAML10P_NS));
149
150                 AttributeQueryConfiguration profileConfig = (AttributeQueryConfiguration) rpConfig
151                         .getProfileConfiguration(AttributeQueryConfiguration.PROFILE_ID);
152                 requestContext.setProfileConfiguration(profileConfig);
153                 if (profileConfig.getSigningCredential() != null) {
154                     requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
155                 } else if (rpConfig.getDefaultSigningCredential() != null) {
156                     requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
157                 }
158
159             } catch (MetadataProviderException e) {
160                 log.error("Unable to locate metadata for asserting or relying party");
161                 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER, null,
162                         "Error locating party metadata"));
163                 throw new ProfileException("Error locating party metadata");
164             }
165         }
166     }
167
168     /**
169      * Encodes the request's SAML response and writes it to the servlet response.
170      * 
171      * @param requestContext current request context
172      * 
173      * @throws ProfileException thrown if no message encoder is registered for this profiles binding
174      */
175     protected void encodeResponse(AttributeQueryContext requestContext) throws ProfileException {
176         if (log.isDebugEnabled()) {
177             log.debug("Encoding response to SAML request " + requestContext.getInboundSAMLMessageId()
178                     + " from relying party " + requestContext.getRelyingPartyEntityId());
179         }
180
181         try {
182             getMessageEncoder().encode(requestContext);
183         } catch (MessageEncodingException e) {
184             throw new ProfileException("Unable to encode response to relying party: "
185                     + requestContext.getRelyingPartyEntityId(), e);
186         }
187     }
188
189     /** Basic data structure used to accumulate information as a request is being processed. */
190     protected class AttributeQueryContext extends
191             BaseSAML1ProfileRequestContext<Request, Response, AttributeQueryConfiguration> {
192
193         /** Current attribute query. */
194         private AttributeQuery attributeQuery;
195
196         /**
197          * Gets the attribute query of the request.
198          * 
199          * @return attribute query of the request
200          */
201         public AttributeQuery getAttributeQuery() {
202             return attributeQuery;
203         }
204
205         /**
206          * Sets the attribute query of the request.
207          * 
208          * @param query attribute query of the request
209          */
210         public void setAttributeQuery(AttributeQuery query) {
211             attributeQuery = query;
212         }
213     }
214 }