fixes
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractSAML2ProfileHandler.java
1 /*
2  * Copyright [2007] [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 java.util.List;
20
21 import javax.servlet.ServletRequest;
22 import javax.servlet.ServletResponse;
23
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;
46
47 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
48
49 /**
50  * Common implementation details for profile handlers.
51  */
52 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
53
54     /** SAML Version for this profile handler. */
55     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
56
57     /** Class logger. */
58     private static Logger log = Logger.getLogger(AbstractSAML2ProfileHandler.class);
59
60     /** For building XML. */
61     private XMLObjectBuilderFactory builderFactory;
62
63     /** For generating random ids. */
64     private IdentifierGenerator idGenerator;
65
66     /** Builder for Response elements. */
67     protected XMLObjectBuilder responseBuilder;
68
69     /** Builder for Status elements. */
70     private XMLObjectBuilder statusBuilder;
71
72     /** Builder for StatusCode elements. */
73     private XMLObjectBuilder statusCodeBuilder;
74
75     /** Builder for StatusMessage elements. */
76     private XMLObjectBuilder statusMessageBuilder;
77
78     /** Builder for Issuer elements. */
79     protected XMLObjectBuilder issuerBuilder;
80
81     /**
82      * Default constructor.
83      */
84     public AbstractSAML2ProfileHandler() {
85         builderFactory = Configuration.getBuilderFactory();
86         idGenerator = new SecureRandomIdentifierGenerator();
87
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);
93     }
94
95     /**
96      * Returns the XML builder factory.
97      * 
98      * @return Returns the builderFactory.
99      */
100     public XMLObjectBuilderFactory getBuilderFactory() {
101         return builderFactory;
102     }
103
104     /**
105      * Returns the id generator.
106      * 
107      * @return Returns the idGenerator.
108      */
109     public IdentifierGenerator getIdGenerator() {
110         return idGenerator;
111     }
112
113     /**
114      * This decodes the attribute query message from the supplied request.
115      * 
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
120      */
121     public static SAMLObject decodeMessage(MessageDecoder<ServletRequest> decoder, ServletRequest request)
122             throws BindingException {
123
124         decoder.setRequest(request);
125         decoder.decode();
126         if (log.isDebugEnabled()) {
127             log.debug("decoded servlet request");
128         }
129
130         return decoder.getSAMLMessage();
131     }
132
133     /**
134      * This encodes the supplied response.
135      * 
136      * @param encoder <code>MessageEncoder</code>
137      * @param response <code>SAMLObject</code>
138      * @throws BindingException if the response cannot be encoded
139      */
140     public static void encodeResponse(MessageEncoder<ServletResponse> encoder, SAMLObject response)
141             throws BindingException {
142
143         encoder.setSAMLMessage(response);
144         encoder.encode();
145     }
146
147     /**
148      * Build a status message, with an optional second-level failure message.
149      * 
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.
154      * 
155      * @return a Status object.
156      */
157     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
158
159         Status status = (Status) statusBuilder.buildObject(Status.DEFAULT_ELEMENT_NAME);
160         StatusCode statusCode = (StatusCode) statusCodeBuilder.buildObject(StatusCode.DEFAULT_ELEMENT_NAME);
161
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);
168         }
169
170         if (secondLevelFailureMessage != null) {
171             StatusMessage msg = (StatusMessage) statusMessageBuilder.buildObject(StatusMessage.DEFAULT_ELEMENT_NAME);
172             msg.setMessage(secondLevelFailureMessage);
173             status.setMessage(msg);
174         }
175
176         return status;
177     }
178
179     /**
180      * Build a SAML 2 Response element with basic fields populated.
181      * 
182      * Failure handlers can send the returned response element to the RP. Success handlers should add the assertions
183      * before sending it.
184      * 
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.
188      * 
189      * @return The populated Response object.
190      */
191     protected Response buildResponse(String inResponseTo, String issuer, final Status status) {
192
193         Response response = (Response) responseBuilder.buildObject(Response.DEFAULT_ELEMENT_NAME);
194
195         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
196         i.setValue(issuer);
197
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);
204
205         return response;
206     }
207
208     protected Assertion buildAssertion(final Subject subject, final Conditions conditions, String issuer,
209             final String[] audiences) {
210
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);
217
218         Issuer i = (Issuer) issuerBuilder.buildObject(Issuer.DEFAULT_ELEMENT_NAME);
219         i.setValue(issuer);
220         assertion.setIssuer(i);
221
222         // if audiences were specified, set an AudienceRestriction condition
223         if (audiences != null && audiences.length > 0) {
224
225             Conditions conditions = assertion.getConditions();
226             List<AudienceRestriction> audienceRestrictionConditions = conditions.getAudienceRestrictions();
227
228             for (String audienceURI : audiences) {
229
230                 Audience audience = (Audience) audienceBuilder.buildObject(Audience.DEFAULT_ELEMENT_NAME);
231                 audience.setAudienceURI(audienceURI);
232
233                 AudienceRestriction audienceRestriction = (AudienceRestriction) audienceRestrictionBuilder
234                         .buildObject(AudienceRestriction.DEFAULT_ELEMENT_NAME);
235                 List<Audience> audienceList = audienceRestriction.getAudiences();
236                 audienceList.add(audience);
237
238                 audienceRestrictionConditions.add(audienceRestriction);
239             }
240         }
241
242         return assertion;
243     }
244 }