3a515876b9c8b33c707596f3734f4a5caf602fc8
[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.SAMLObjectBuilder;
29 import org.opensaml.common.SAMLVersion;
30 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
31 import org.opensaml.saml2.core.AuthnContext;
32 import org.opensaml.saml2.core.AuthnContextClassRef;
33 import org.opensaml.saml2.core.AuthnContextDeclRef;
34 import org.opensaml.saml2.core.AuthnStatement;
35 import org.opensaml.saml2.core.Assertion;
36 import org.opensaml.saml2.core.Audience;
37 import org.opensaml.saml2.core.AudienceRestriction;
38 import org.opensaml.saml2.core.AuthnStatement;
39 import org.opensaml.saml2.core.Conditions;
40 import org.opensaml.saml2.core.Issuer;
41 import org.opensaml.saml2.core.Response;
42 import org.opensaml.saml2.core.Status;
43 import org.opensaml.saml2.core.StatusCode;
44 import org.opensaml.saml2.core.StatusMessage;
45 import org.opensaml.saml2.core.Subject;
46 import org.opensaml.xml.XMLObjectBuilderFactory;
47
48 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
49
50 /**
51  * Common implementation details for profile handlers.
52  */
53 public abstract class AbstractSAML2ProfileHandler extends
54                 AbstractSAMLProfileHandler {
55
56         /** SAML Version for this profile handler. */
57         public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
58
59         /** URI for the SAML 2 protocol. */
60         public static final String SAML20_PROTOCOL_URI = "urn:oasis:names:tc:SAML:2.0:protocol";
61
62         /** Class logger. */
63         private static Logger log = Logger
64                         .getLogger(AbstractSAML2ProfileHandler.class);
65
66         /** For building XML. */
67         private XMLObjectBuilderFactory builderFactory;
68
69         /** For generating random ids. */
70         private IdentifierGenerator idGenerator;
71
72         /** Builder for Response elements. */
73         protected SAMLObjectBuilder<Response> responseBuilder;
74
75         /** Builder for Status elements. */
76         protected SAMLObjectBuilder<Status> statusBuilder;
77
78         /** Builder for StatusCode elements. */
79         protected SAMLObjectBuilder<StatusCode> statusCodeBuilder;
80
81         /** Builder for StatusMessage elements. */
82         protected SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
83
84         /** Builder for Issuer elements. */
85         protected SAMLObjectBuilder<Issuer> issuerBuilder;
86
87         /** Builder for Assertion elements. */
88         protected SAMLObjectBuilder<Assertion> assertionBuilder;
89
90         /** Builder for Condition elements. */
91         protected SAMLObjectBuilder<Conditions> conditionsBuilder;
92
93         /** Builder for AuthnStatement elements. */
94         protected SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
95
96         /** Builder for AuthnContext elements. */
97         protected SAMLObjectBuilder<AuthnContext> authnContextBuilder;
98
99         /** Builder for AuthnContextClassRef elements. */
100         protected SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
101
102         /** Builder for AuthnContextDeclRef elements. */
103         protected SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
104
105         /** Builder for AudienceRestriction conditions. */
106         protected SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
107
108         /** Builder for Audience elemenets. */
109         protected SAMLObjectBuilder<Audience> audienceBuilder;
110
111         /**
112          * Default constructor.
113          */
114         public AbstractSAML2ProfileHandler() {
115                 builderFactory = Configuration.getBuilderFactory();
116                 idGenerator = new SecureRandomIdentifierGenerator();
117
118                 assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
119                                 .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
120                 authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory()
121                                 .getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
122                 authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory()
123                                 .getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
124                 authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory()
125                                 .getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
126                 authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory()
127                                 .getBuilder(AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
128                 audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory()
129                                 .getBuilder(AudienceRestriction.DEFAULT_ELEMENT_NAME);
130                 audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory()
131                                 .getBuilder(Audience.DEFAULT_ELEMENT_NAME);
132                 conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory()
133                                 .getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
134                 responseBuilder = (SAMLObjectBuilder<Response>) builderFactory
135                                 .getBuilder(Response.DEFAULT_ELEMENT_NAME);
136                 statusBuilder = (SAMLObjectBuilder<Status>) builderFactory
137                                 .getBuilder(Status.DEFAULT_ELEMENT_NAME);
138                 statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) builderFactory
139                                 .getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
140                 statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) builderFactory
141                                 .getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
142                 issuerBuilder = (SAMLObjectBuilder<Issuer>) builderFactory
143                                 .getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
144         }
145
146         /**
147          * Returns the XML builder factory.
148          * 
149          * @return Returns the builderFactory.
150          */
151         public XMLObjectBuilderFactory getBuilderFactory() {
152                 return builderFactory;
153         }
154
155         /**
156          * Returns the id generator.
157          * 
158          * @return Returns the idGenerator.
159          */
160         public IdentifierGenerator getIdGenerator() {
161                 return idGenerator;
162         }
163
164         /**
165          * Build a status message, with an optional second-level failure message.
166          * 
167          * @param topLevelCode
168          *            The top-level status code. Should be from saml-core-2.0-os,
169          *            sec. 3.2.2.2
170          * @param secondLevelCode
171          *            An optional second-level failure code. Should be from
172          *            saml-core-2.0-is, sec 3.2.2.2. If null, no second-level Status
173          *            element will be set.
174          * @param secondLevelFailureMessage
175          *            An optional second-level failure message.
176          * 
177          * @return a Status object.
178          */
179         protected Status buildStatus(String topLevelCode, String secondLevelCode,
180                         String secondLevelFailureMessage) {
181
182                 Status status = statusBuilder.buildObject();
183                 StatusCode statusCode = statusCodeBuilder.buildObject();
184
185                 statusCode.setValue(topLevelCode);
186                 if (secondLevelCode != null) {
187                         StatusCode secondLevelStatusCode = statusCodeBuilder.buildObject();
188                         secondLevelStatusCode.setValue(secondLevelCode);
189                         statusCode.setStatusCode(secondLevelStatusCode);
190                 }
191
192                 if (secondLevelFailureMessage != null) {
193                         StatusMessage msg = statusMessageBuilder.buildObject();
194                         msg.setMessage(secondLevelFailureMessage);
195                         status.setStatusMessage(msg);
196                 }
197
198                 return status;
199         }
200
201         /**
202          * Build a status message, with an optional second-level failure message.
203          * 
204          * @param topLevelCode
205          *            The top-level status code. Should be from saml-core-2.0-os,
206          *            sec. 3.2.2.2
207          * @param secondLevelCode
208          *            An optional second-level failure code. Should be from
209          *            saml-core-2.0-is, sec 3.2.2.2. If null, no second-level Status
210          *            element will be set.
211          * 
212          * @return a Status object.
213          */
214         protected Status buildStatus(String topLevelCode,
215                         final StatusCode secondLevelCode) {
216
217                 Status status = statusBuilder.buildObject();
218                 StatusCode statusCode = statusCodeBuilder.buildObject();
219
220                 statusCode.setValue(topLevelCode);
221                 if (secondLevelCode != null) {
222                         statusCode.setStatusCode(secondLevelCode);
223                 }
224
225                 return status;
226         }
227
228         /**
229          * Build a StatusCode.
230          * 
231          * @param statusCode
232          *            The URI status code.
233          * @param message
234          *            The message; may be <code>null</code.
235          *
236          * @return a StatusCode object.
237          */
238         protected StatusCode buildStatusCode(String statusCode) {
239                 return null;
240         }
241
242         /**
243          * Build a SAML 2 Response element with basic fields populated.
244          * 
245          * Failure handlers can send the returned response element to the RP.
246          * Success handlers should add the assertions before sending it.
247          * 
248          * @param inResponseTo
249          *            The ID of the request this is in response to.
250          * @param issueInstant
251          *            The timestamp of this response.
252          * @param issuer
253          *            The URI of the RP issuing the response.
254          * @param status
255          *            The response's status code.
256          * 
257          * @return The populated Response object.
258          */
259         protected Response buildResponse(String inResponseTo,
260                         final DateTime issueInstant, String issuer, final Status status) {
261
262                 Response response = responseBuilder.buildObject();
263
264                 Issuer i = issuerBuilder.buildObject();
265                 i.setValue(issuer);
266
267                 response.setVersion(SAML_VERSION);
268                 response.setID(getIdGenerator().generateIdentifier());
269                 response.setInResponseTo(inResponseTo);
270                 response.setIssueInstant(issueInstant);
271                 response.setIssuer(i);
272                 response.setStatus(status);
273
274                 return response;
275         }
276
277         /**
278          * Build a skeletal SAML 2 assertion.
279          * 
280          * Note, the caller may either set the audiences in the conditions argument,
281          * or pass a list of URIs to this method. If the latter option is chosen,
282          * this method will create the appropriate AudienceRestriction element.
283          * 
284          * @param subject
285          *            The Subject of the assertion.
286          * @param conditions
287          *            The conditions object.
288          * @param issuer
289          *            The URI of the RP issuing the assertion.
290          * @param audiences
291          *            A possibly null array of audience URIs for the assertion.
292          * 
293          * @return The assertion object.
294          */
295         protected Assertion buildAssertion(final Subject subject,
296                         final Conditions conditions, final Issuer issuer,
297                         final String[] audiences) {
298
299                 Assertion assertion = assertionBuilder.buildObject();
300                 assertion.setID(getIdGenerator().generateIdentifier());
301                 assertion.setVersion(SAML_VERSION);
302                 assertion.setIssueInstant(new DateTime());
303                 assertion.setConditions(conditions);
304                 assertion.setSubject(subject);
305
306                 Issuer i = issuerBuilder.buildObject();
307                 i.setValue(issuer.getValue());
308                 assertion.setIssuer(i);
309
310                 // if audiences were specified, set an AudienceRestriction condition
311                 if (audiences != null && audiences.length > 0) {
312
313                         List<AudienceRestriction> audienceRestrictionConditions = assertion
314                                         .getConditions().getAudienceRestrictions();
315
316                         AudienceRestriction audienceRestriction = audienceRestrictionBuilder
317                                         .buildObject();
318                         audienceRestrictionConditions.add(audienceRestriction);
319
320                         List<Audience> audienceList = audienceRestriction.getAudiences();
321
322                         for (String audienceURI : audiences) {
323                                 Audience audience = audienceBuilder.buildObject();
324                                 audience.setAudienceURI(audienceURI);
325                                 audienceList.add(audience);
326                         }
327                 }
328
329                 return assertion;
330         }
331 }