2 * Copyright [2007] [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.List;
21 import javax.servlet.ServletRequest;
22 import javax.servlet.ServletResponse;
24 import org.apache.log4j.Logger;
25 import org.joda.time.DateTime;
26 import org.opensaml.Configuration;
27 import org.opensaml.common.IdentifierGenerator;
28 import org.opensaml.common.SAMLObject;
29 import org.opensaml.common.SAMLVersion;
30 import org.opensaml.common.binding.BindingException;
31 import org.opensaml.common.binding.MessageDecoder;
32 import org.opensaml.common.binding.MessageEncoder;
33 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
34 import org.opensaml.saml2.core.Assertion;
35 import org.opensaml.saml2.core.Audience;
36 import org.opensaml.saml2.core.AudienceRestriction;
37 import org.opensaml.saml2.core.Conditions;
38 import org.opensaml.saml2.core.Issuer;
39 import org.opensaml.saml2.core.Response;
40 import org.opensaml.saml2.core.Status;
41 import org.opensaml.saml2.core.StatusCode;
42 import org.opensaml.saml2.core.StatusMessage;
43 import org.opensaml.saml2.core.Subject;
44 import org.opensaml.xml.XMLObjectBuilder;
45 import org.opensaml.xml.XMLObjectBuilderFactory;
47 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
50 * Common implementation details for profile handlers.
52 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
54 /** SAML Version for this profile handler. */
55 public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
58 private static Logger log = Logger.getLogger(AbstractSAML2ProfileHandler.class);
60 /** For building XML. */
61 private XMLObjectBuilderFactory builderFactory;
63 /** For generating random ids. */
64 private IdentifierGenerator idGenerator;
66 /** Builder for Response elements. */
67 protected XMLObjectBuilder responseBuilder;
69 /** Builder for Status elements. */
70 private XMLObjectBuilder statusBuilder;
72 /** Builder for StatusCode elements. */
73 private XMLObjectBuilder statusCodeBuilder;
75 /** Builder for StatusMessage elements. */
76 private XMLObjectBuilder statusMessageBuilder;
78 /** Builder for Issuer elements. */
79 protected XMLObjectBuilder issuerBuilder;
82 * Default constructor.
84 public AbstractSAML2ProfileHandler() {
85 builderFactory = Configuration.getBuilderFactory();
86 idGenerator = new SecureRandomIdentifierGenerator();
88 responseBuilder = builderFactory.getBuilder(Response.DEFAULT_ELEMENT_NAME);
89 statusBuilder = builderFactory.getBuilder(Status.DEFAULT_ELEMENT_NAME);
90 statusCodeBuilder = builderFactory.getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
91 statusMessageBuilder = builderFactory.getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
92 issuerBuilder = builderFactory.getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
96 * Returns the XML builder factory.
98 * @return Returns the builderFactory.
100 public XMLObjectBuilderFactory getBuilderFactory() {
101 return builderFactory;
105 * Returns the id generator.
107 * @return Returns the idGenerator.
109 public IdentifierGenerator getIdGenerator() {
114 * This decodes the attribute query message from the supplied request.
116 * @param decoder <code>MessageDecoder</code>
117 * @param request <code>ServletRequest</code>
118 * @return <code>SAMLObject</code>
119 * @throws BindingException if the request cannot be decoded
121 public static SAMLObject decodeMessage(MessageDecoder<ServletRequest> decoder, ServletRequest request)
122 throws BindingException {
124 decoder.setRequest(request);
126 if (log.isDebugEnabled()) {
127 log.debug("decoded servlet request");
130 return decoder.getSAMLMessage();
134 * This encodes the supplied response.
136 * @param encoder <code>MessageEncoder</code>
137 * @param response <code>SAMLObject</code>
138 * @throws BindingException if the response cannot be encoded
140 public static void encodeResponse(MessageEncoder<ServletResponse> encoder, SAMLObject response)
141 throws BindingException {
143 encoder.setSAMLMessage(response);
148 * Build a status message, with an optional second-level failure message.
150 * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
151 * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
152 * null, no second-level Status element will be set.
153 * @param secondLevelFailureMessage An optional second-level failure message.
155 * @return a Status object.
157 protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
159 Status status = (Status) statusBuilder.buildObject(Status.DEFAULT_ELEMENT_NAME);
160 StatusCode statusCode = (StatusCode) statusCodeBuilder.buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
162 statusCode.setValue(topLevelCode);
163 if (secondLevelCode != null) {
164 StatusCode secondLevelStatusCode = (StatusCode) statusCodeBuilder
165 .buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
166 secondLevelStatusCode.setValue(secondLevelCode);
167 statusCode.setStatusCode(secondLevelStatusCode);
170 if (secondLevelFailureMessage != null) {
171 StatusMessage msg = (StatusMessage) statusMessageBuilder.buildObject(StatusMessage.DEFAULT_ELEMENT_NAME);
172 msg.setMessage(secondLevelFailureMessage);
173 status.setMessage(msg);
180 * Build a SAML 2 Response element with basic fields populated.
182 * Failure handlers can send the returned response element to the RP. Success handlers should add the assertions
185 * @param inResponseTo The ID of the request this is in response to.
186 * @param issuer The URI of the RP issuing the response.
187 * @param status The response's status code.
189 * @return The populated Response object.
191 protected Response buildResponse(String inResponseTo, String issuer, final Status status) {
193 Response response = (Response) responseBuilder.buildObject(Response.DEFAULT_ELEMENT_NAME);
195 Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
198 response.setVersion(SAML_VERSION);
199 response.setId(getIdGenerator().generateIdentifier());
200 response.setInResponseto(inResponseTo);
201 response.setIssueInstance(new DateTime());
202 response.setIssuer(i);
203 response.setStatus(status);
208 protected Assertion buildAssertion(final Subject subject, final Conditions conditions, String issuer,
209 final String[] audiences) {
211 Assertion assertion = (Assertion) assertionBuilder.buildObject(Assertion.DEFAULT_ELEMENT_NAME);
212 assertion.setID(getIdGenerator().generateIdentifier());
213 assertion.setVersion(SAML_VERSION);
214 assertion.setIssueInstant(new DateTime());
215 assertion.setConditions(conditions);
216 assertion.setSubject(subject);
218 Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
220 assertion.setIssuer(i);
222 // if audiences were specified, set an AudienceRestriction condition
223 if (audiences != null && audiences.length > 0) {
225 Conditions conditions = assertion.getConditions();
226 List<AudienceRestriction> audienceRestrictionConditions = conditions.getAudienceRestrictions();
228 for (String audienceURI : audiences) {
230 Audience audience = (Audience) audienceBuilder.buildObject(Audience.DEFAULT_ELEMENT_NAME);
231 audience.setAudienceURI(audienceURI);
233 AudienceRestriction audienceRestriction = (AudienceRestriction) audienceRestrictionBuilder
234 .buildObject(AudienceRestriction.DEFAULT_ELEMENT_NAME);
235 List<Audience> audienceList = audienceRestriction.getAudiences();
236 audienceList.add(audience);
238 audienceRestrictionConditions.add(audienceRestriction);