2 * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
19 import java.util.ArrayList;
21 import javax.servlet.ServletRequest;
22 import javax.servlet.ServletResponse;
24 import org.apache.log4j.Logger;
25 import org.opensaml.common.binding.BindingException;
26 import org.opensaml.common.binding.decoding.MessageDecoder;
27 import org.opensaml.common.binding.encoding.MessageEncoder;
28 import org.opensaml.common.binding.security.SAMLSecurityPolicy;
29 import org.opensaml.common.xml.SAMLConstants;
30 import org.opensaml.saml2.binding.decoding.HTTPSOAP11Decoder;
31 import org.opensaml.saml2.core.AttributeQuery;
32 import org.opensaml.saml2.core.Response;
33 import org.opensaml.saml2.core.Statement;
34 import org.opensaml.saml2.core.StatusCode;
35 import org.opensaml.saml2.core.Subject;
36 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
37 import org.opensaml.ws.security.SecurityPolicyException;
39 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
40 import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
41 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
42 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
43 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AttributeQueryConfiguration;
45 /** SAML 2.0 Attribute Query profile handler. */
46 public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
49 private static Logger log = Logger.getLogger(AttributeQueryProfileHandler.class);
51 /** SAML binding URI. */
52 private static final String BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP";
55 public String getProfileId() {
56 return "urn:mace:shibboleth:2.0:idp:profiles:saml2:query:attribute";
60 public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
61 throws ProfileException {
63 AttributeQueryContext requestContext = new AttributeQueryContext(request, response);
65 Response samlResponse;
67 decodeRequest(requestContext);
69 if (requestContext.getRelyingPartyConfiguration() == null) {
70 log.error("SAML 2 Attribute Query profile is not configured for relying party "
71 + requestContext.getRelyingPartyId());
72 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
73 "SAML 2 Attribute Query profile is not configured for relying party "
74 + requestContext.getRelyingPartyId()));
75 samlResponse = buildErrorResponse(requestContext);
78 checkSamlVersion(requestContext);
80 // Resolve attribute query name id to principal name and place in context
81 resolvePrincipal(requestContext);
83 // Lookup principal name and attributes, create attribute statement from information
84 ArrayList<Statement> statements = new ArrayList<Statement>();
85 statements.add(buildAttributeStatement(requestContext));
87 // create the assertion subject
88 Subject assertionSubject = buildSubject(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches");
90 // create the SAML response
91 samlResponse = buildResponse(requestContext, assertionSubject, statements);
92 } catch (ProfileException e) {
93 samlResponse = buildErrorResponse(requestContext);
96 requestContext.setSamlResponse(samlResponse);
98 encodeResponse(requestContext);
99 writeAuditLogEntry(requestContext);
103 * Decodes the message in the request and adds it to the request context.
105 * @param requestContext request context contianing the request to decode
107 * @throws ProfileException throw if there is a problem decoding the request
109 protected void decodeRequest(AttributeQueryContext requestContext) throws ProfileException {
110 if (log.isDebugEnabled()) {
111 log.debug("Decoding incomming request");
113 MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(
114 HTTPSOAP11Decoder.BINDING_URI);
115 if (decoder == null) {
116 throw new ProfileException("No request decoder was registered for binding type: "
117 + HTTPSOAP11Decoder.BINDING_URI);
119 super.populateMessageDecoder(decoder);
121 ProfileRequest<ServletRequest> profileRequest = requestContext.getProfileRequest();
122 decoder.setRequest(profileRequest.getRawRequest());
123 requestContext.setMessageDecoder(decoder);
127 if (log.isDebugEnabled()) {
128 log.debug("Decoded request");
130 } catch (BindingException e) {
131 log.error("Error decoding attribute query message", e);
132 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Error decoding message"));
133 throw new ProfileException("Error decoding attribute query message");
134 } catch (SecurityPolicyException e) {
135 log.error("Message did not meet security policy requirements", e);
136 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
137 "Message did not meet security policy requirements"));
138 throw new ProfileException("Message did not meet security policy requirements", e);
140 // Set as much information as can be retrieved from the decoded message
141 SAMLSecurityPolicy securityPolicy = requestContext.getMessageDecoder().getSecurityPolicy();
142 requestContext.setRelyingPartyId(securityPolicy.getIssuer());
145 requestContext.setRelyingPartyMetadata(getMetadataProvider().getEntityDescriptor(
146 requestContext.getRelyingPartyId()));
148 requestContext.setRelyingPartyRoleMetadata(requestContext.getRelyingPartyMetadata().getSPSSODescriptor(
149 SAMLConstants.SAML20P_NS));
151 RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(requestContext.getRelyingPartyId());
152 requestContext.setRelyingPartyConfiguration(rpConfig);
154 requestContext.setAssertingPartyId(requestContext.getRelyingPartyConfiguration().getProviderId());
156 requestContext.setAssertingPartyMetadata(getMetadataProvider().getEntityDescriptor(
157 requestContext.getAssertingPartyId()));
159 requestContext.setAssertingPartyRoleMetadata(requestContext.getAssertingPartyMetadata()
160 .getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS));
162 requestContext.setProfileConfiguration((AttributeQueryConfiguration) rpConfig
163 .getProfileConfiguration(AttributeQueryConfiguration.PROFILE_ID));
165 requestContext.setSamlRequest((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
166 } catch (MetadataProviderException e) {
167 log.error("Unable to locate metadata for asserting or relying party");
168 requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
169 "Error locating party metadata"));
170 throw new ProfileException("Error locating party metadata");
176 * Encodes the request's SAML response and writes it to the servlet response.
178 * @param requestContext current request context
180 * @throws ProfileException thrown if no message encoder is registered for this profiles binding
182 protected void encodeResponse(AttributeQueryContext requestContext) throws ProfileException {
183 if (log.isDebugEnabled()) {
184 log.debug("Encoding response to SAML request " + requestContext.getSamlRequest().getID()
185 + " from relying party " + requestContext.getRelyingPartyId());
187 MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(BINDING);
188 if (encoder == null) {
189 throw new ProfileException("No response encoder was registered for binding type: " + BINDING);
192 super.populateMessageEncoder(encoder);
193 encoder.setRelayState(requestContext.getMessageDecoder().getRelayState());
194 ProfileResponse<ServletResponse> profileResponse = requestContext.getProfileResponse();
195 encoder.setResponse(profileResponse.getRawResponse());
196 encoder.setSamlMessage(requestContext.getSamlResponse());
197 requestContext.setMessageEncoder(encoder);
201 } catch (BindingException e) {
202 throw new ProfileException("Unable to encode response to relying party: "
203 + requestContext.getRelyingPartyId(), e);
207 /** Basic data structure used to accumulate information as a request is being processed. */
208 protected class AttributeQueryContext extends
209 SAML2ProfileRequestContext<AttributeQuery, Response, AttributeQueryConfiguration> {
214 * @param request current profile request
215 * @param response current profile response
217 public AttributeQueryContext(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) {
218 super(request, response);