Overwrite existing code
[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.Collection;
20
21 import org.joda.time.DateTime;
22 import org.opensaml.common.SAMLObjectBuilder;
23 import org.opensaml.common.SAMLVersion;
24 import org.opensaml.common.impl.SAMLObjectContentReference;
25 import org.opensaml.saml2.core.Advice;
26 import org.opensaml.saml2.core.Assertion;
27 import org.opensaml.saml2.core.Audience;
28 import org.opensaml.saml2.core.AudienceRestriction;
29 import org.opensaml.saml2.core.Conditions;
30 import org.opensaml.saml2.core.Issuer;
31 import org.opensaml.saml2.core.ProxyRestriction;
32 import org.opensaml.saml2.core.RequestAbstractType;
33 import org.opensaml.saml2.core.Response;
34 import org.opensaml.saml2.core.Status;
35 import org.opensaml.saml2.core.StatusCode;
36 import org.opensaml.saml2.core.StatusMessage;
37 import org.opensaml.saml2.core.StatusResponseType;
38 import org.opensaml.saml2.core.Subject;
39 import org.opensaml.xml.XMLObjectBuilder;
40 import org.opensaml.xml.encryption.EncryptionException;
41 import org.opensaml.xml.security.credential.Credential;
42 import org.opensaml.xml.signature.Signature;
43 import org.opensaml.xml.signature.Signer;
44 import org.opensaml.xml.util.DatatypeHelper;
45
46 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
47 import edu.internet2.middleware.shibboleth.common.relyingparty.saml2.AbstractSAML2ProfileConfiguration;
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 AbstractSAMLProfileHandler {
54
55     /** For building response. */
56     private SAMLObjectBuilder<Response> responseBuilder;
57
58     /** For building status. */
59     private SAMLObjectBuilder<Status> statusBuilder;
60
61     /** For building statuscode. */
62     private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
63
64     /** For building StatusMessages. */
65     private SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
66
67     /** For building assertion. */
68     private SAMLObjectBuilder<Assertion> assertionBuilder;
69
70     /** For building issuer. */
71     private SAMLObjectBuilder<Issuer> issuerBuilder;
72
73     /** For building subject. */
74     private SAMLObjectBuilder<Subject> subjectBuilder;
75
76     /** For building conditions. */
77     private SAMLObjectBuilder<Conditions> conditionsBuilder;
78
79     /** For building audience restriction. */
80     private SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
81
82     /** For building proxy retrictions. */
83     private SAMLObjectBuilder<ProxyRestriction> proxyRestrictionBuilder;
84
85     /** For building audience. */
86     private SAMLObjectBuilder<Audience> audienceBuilder;
87
88     /** For building advice. */
89     private SAMLObjectBuilder<Advice> adviceBuilder;
90
91     /** For building signature. */
92     private XMLObjectBuilder<Signature> signatureBuilder;
93
94     /** Constructor. */
95     @SuppressWarnings("unchecked")
96     protected AbstractSAML2ProfileHandler() {
97         super();
98
99         responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
100         statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
101         statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
102                 StatusCode.DEFAULT_ELEMENT_NAME);
103         statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(
104                 StatusMessage.DEFAULT_ELEMENT_NAME);
105         issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
106         assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
107                 .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
108         subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
109         conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
110                 Conditions.DEFAULT_ELEMENT_NAME);
111         audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory().getBuilder(
112                 AudienceRestriction.DEFAULT_ELEMENT_NAME);
113         proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>) getBuilderFactory().getBuilder(
114                 ProxyRestriction.DEFAULT_ELEMENT_NAME);
115         audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
116         adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
117         signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
118     }
119
120     /**
121      * Convenience method for getting the SAML 2 advice builder.
122      * 
123      * @return SAML 2 advice builder
124      */
125     public SAMLObjectBuilder<Advice> getAdviceBuilder() {
126         return adviceBuilder;
127     }
128
129     /**
130      * Convenience method for getting the SAML 2 assertion builder.
131      * 
132      * @return SAML 2 assertion builder
133      */
134     public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
135         return assertionBuilder;
136     }
137
138     /**
139      * Convenience method for getting the SAML 2 audience builder.
140      * 
141      * @return SAML 2 audience builder
142      */
143     public SAMLObjectBuilder<Audience> getAudienceBuilder() {
144         return audienceBuilder;
145     }
146
147     /**
148      * Convenience method for getting the SAML 2 audience restriction builder.
149      * 
150      * @return SAML 2 audience restriction builder
151      */
152     public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
153         return audienceRestrictionBuilder;
154     }
155
156     /**
157      * Convenience method for getting the SAML 2 conditions builder.
158      * 
159      * @return SAML 2 conditions builder
160      */
161     public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
162         return conditionsBuilder;
163     }
164
165     /**
166      * Convenience method for getting the SAML 2 Issuer builder.
167      * 
168      * @return SAML 2 Issuer builder
169      */
170     public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
171         return issuerBuilder;
172     }
173
174     /**
175      * Convenience method for getting the SAML 2 proxy restriction builder.
176      * 
177      * @return SAML 2 proxy restriction builder
178      */
179     public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
180         return proxyRestrictionBuilder;
181     }
182
183     /**
184      * Convenience method for getting the SAML 2 response builder.
185      * 
186      * @return SAML 2 response builder
187      */
188     public SAMLObjectBuilder<Response> getResponseBuilder() {
189         return responseBuilder;
190     }
191
192     /**
193      * Convenience method for getting the Signature builder.
194      * 
195      * @return signature builder
196      */
197     public XMLObjectBuilder<Signature> getSignatureBuilder() {
198         return signatureBuilder;
199     }
200
201     /**
202      * Convenience method for getting the SAML 2 status builder.
203      * 
204      * @return SAML 2 status builder
205      */
206     public SAMLObjectBuilder<Status> getStatusBuilder() {
207         return statusBuilder;
208     }
209
210     /**
211      * Convenience method for getting the SAML 2 status code builder.
212      * 
213      * @return SAML 2 status code builder
214      */
215     public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
216         return statusCodeBuilder;
217     }
218
219     /**
220      * Convenience method for getting the SAML 2 status message builder.
221      * 
222      * @return SAML 2 status message builder
223      */
224     public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
225         return statusMessageBuilder;
226     }
227
228     /**
229      * Convenience method for getting the SAML 2 subject builder.
230      * 
231      * @return SAML 2 subject builder
232      */
233     public SAMLObjectBuilder<Subject> getSubjectBuilder() {
234         return subjectBuilder;
235     }
236
237     /**
238      * Populates the response's id, in response to, issue instant, version, and issuer properties.
239      * 
240      * @param response the response to populate
241      * @param issueInstant timestamp to use as the issue instant for the response
242      * @param request the request that the response is for
243      * @param rpConfig the relying party configuration for the request
244      */
245     protected void populateStatusResponse(StatusResponseType response, DateTime issueInstant,
246             RequestAbstractType request, RelyingPartyConfiguration rpConfig) {
247         response.setID(getIdGenerator().generateIdentifier());
248         response.setInResponseTo(request.getID());
249         response.setIssueInstant(issueInstant);
250         response.setVersion(SAMLVersion.VERSION_20);
251         response.setIssuer(buildEntityIssuer(rpConfig));
252     }
253
254     /**
255      * Builds a {@link Status} object populated with the given code and message.
256      * 
257      * @param statusCode status code or null
258      * @param statusMessage status message or null
259      * 
260      * @return built status object
261      */
262     protected Status buildStatus(String statusCode, String statusMessage) {
263         Status status = getStatusBuilder().buildObject();
264
265         String trimmedCode = DatatypeHelper.safeTrimOrNullString(statusCode);
266         if (trimmedCode != null) {
267             StatusCode code = getStatusCodeBuilder().buildObject();
268             code.setValue(trimmedCode);
269             status.setStatusCode(code);
270         }
271
272         String trimmedMessage = DatatypeHelper.safeTrimOrNullString(statusMessage);
273         if (trimmedMessage != null) {
274             StatusMessage message = getStatusMessageBuilder().buildObject();
275             message.setMessage(trimmedMessage);
276             status.setStatusMessage(message);
277         }
278
279         return status;
280     }
281
282     /**
283      * Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
284      * 
285      * @param issueInstant time to use as assertion issue instant
286      * @param rpConfig the relying party configuration
287      * @param profileConfig current profile configuration
288      * 
289      * @return the built assertion
290      */
291     protected Assertion buildAssertion(DateTime issueInstant, RelyingPartyConfiguration rpConfig,
292             AbstractSAML2ProfileConfiguration profileConfig) {
293         Assertion assertion = assertionBuilder.buildObject();
294         assertion.setID(getIdGenerator().generateIdentifier());
295         assertion.setIssueInstant(issueInstant);
296         assertion.setVersion(SAMLVersion.VERSION_20);
297         assertion.setIssuer(buildEntityIssuer(rpConfig));
298         //TODO assertion.setSubject(buildSubject());
299
300         Conditions conditions = buildConditions(issueInstant, profileConfig);
301         assertion.setConditions(conditions);
302
303         return assertion;
304     }
305
306     /**
307      * Builds an entity type Issuer populated with the correct provider Id for this relying party configuration.
308      * 
309      * @param rpConfig the relying party configuration
310      * 
311      * @return the built Issuer
312      */
313     protected Issuer buildEntityIssuer(RelyingPartyConfiguration rpConfig) {
314         Issuer issuer = getIssuerBuilder().buildObject();
315         issuer.setFormat(Issuer.ENTITY);
316         issuer.setValue(rpConfig.getProviderId());
317
318         return issuer;
319     }
320
321     /**
322      * Builds the SAML subject for the user for the service provider.
323      * 
324      * @return SAML subject for the user for the service provider
325      * 
326      * @throws EncryptionException thrown if there is a problem encryption the subject's NameID
327      */
328     protected Subject buildSubject() throws EncryptionException {
329         // TODO
330         return null;
331     }
332
333     /**
334      * Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
335      * restrictions, and proxy restrictions.
336      * 
337      * @param issueInstant timestamp the assertion was created
338      * @param profileConfig current profile configuration
339      * 
340      * @return constructed conditions
341      */
342     private Conditions buildConditions(DateTime issueInstant, AbstractSAML2ProfileConfiguration profileConfig) {
343         Conditions conditions = conditionsBuilder.buildObject();
344         conditions.setNotBefore(issueInstant);
345         conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
346
347         Collection<String> audiences;
348
349         // add audience restrictions
350         audiences = profileConfig.getAssertionAudiences();
351         if (audiences != null && audiences.size() > 0) {
352             AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
353             Audience audience;
354             for (String audienceUri : audiences) {
355                 audience = audienceBuilder.buildObject();
356                 audience.setAudienceURI(audienceUri);
357                 audienceRestriction.getAudiences().add(audience);
358             }
359             conditions.getAudienceRestrictions().add(audienceRestriction);
360         }
361
362         // add proxy restrictions
363         audiences = profileConfig.getProxyAudiences();
364         if (audiences != null && audiences.size() > 0) {
365             ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
366             Audience audience;
367             for (String audienceUri : audiences) {
368                 audience = audienceBuilder.buildObject();
369                 audience.setAudienceURI(audienceUri);
370                 proxyRestriction.getAudiences().add(audience);
371             }
372
373             proxyRestriction.setProxyCount(profileConfig.getProxyCount());
374             conditions.getConditions().add(proxyRestriction);
375         }
376
377         return conditions;
378     }
379
380     /**
381      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
382      * signing credentials.
383      * 
384      * @param assertion assertion to sign
385      * @param rpConfig relying party configuration
386      * @param profileConfig current profile configuration
387      */
388     protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
389             AbstractSAML2ProfileConfiguration profileConfig) {
390         if (!profileConfig.getSignAssertions()) {
391             return;
392         }
393
394         Credential signatureCredential = profileConfig.getSigningCredential();
395         if (signatureCredential == null) {
396             signatureCredential = rpConfig.getDefaultSigningCredential();
397         }
398
399         if (signatureCredential == null) {
400             return;
401         }
402
403         SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
404         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
405         signature.getContentReferences().add(contentRef);
406         assertion.setSignature(signature);
407
408         Signer.signObject(signature);
409     }
410     
411     protected void signResponse(StatusResponseType response, RelyingPartyConfiguration rpConfig, AbstractSAML2ProfileConfiguration profileConfig){
412         if (!profileConfig.getSignResponses()) {
413             return;
414         }
415
416         Credential signatureCredential = profileConfig.getSigningCredential();
417         if (signatureCredential == null) {
418             signatureCredential = rpConfig.getDefaultSigningCredential();
419         }
420
421         if (signatureCredential == null) {
422             return;
423         }
424
425         SAMLObjectContentReference contentRef = new SAMLObjectContentReference(response);
426         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
427         signature.getContentReferences().add(contentRef);
428         response.setSignature(signature);
429
430         Signer.signObject(signature);
431     }
432
433     // TODO encryption support
434 }