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