Initial attribute query test, along with bugfixes, where Issuer is not authenticated
[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.provider.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(
112                 StatusCode.DEFAULT_ELEMENT_NAME);
113         statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(
114                 StatusMessage.DEFAULT_ELEMENT_NAME);
115         issuerBuilder = (SAMLObjectBuilder<Issuer>) getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
116         assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
117                 .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
118         subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
119         conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
120                 Conditions.DEFAULT_ELEMENT_NAME);
121         audienceRestrictionBuilder = (SAMLObjectBuilder<AudienceRestriction>) getBuilderFactory().getBuilder(
122                 AudienceRestriction.DEFAULT_ELEMENT_NAME);
123         proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>) getBuilderFactory().getBuilder(
124                 ProxyRestriction.DEFAULT_ELEMENT_NAME);
125         audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
126         adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
127         signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
128     }
129
130     /**
131      * Convenience method for getting the SAML 2 advice builder.
132      * 
133      * @return SAML 2 advice builder
134      */
135     public SAMLObjectBuilder<Advice> getAdviceBuilder() {
136         return adviceBuilder;
137     }
138
139     /**
140      * Convenience method for getting the SAML 2 assertion builder.
141      * 
142      * @return SAML 2 assertion builder
143      */
144     public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
145         return assertionBuilder;
146     }
147
148     /**
149      * Convenience method for getting the SAML 2 audience builder.
150      * 
151      * @return SAML 2 audience builder
152      */
153     public SAMLObjectBuilder<Audience> getAudienceBuilder() {
154         return audienceBuilder;
155     }
156
157     /**
158      * Convenience method for getting the SAML 2 audience restriction builder.
159      * 
160      * @return SAML 2 audience restriction builder
161      */
162     public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
163         return audienceRestrictionBuilder;
164     }
165
166     /**
167      * Convenience method for getting the SAML 2 conditions builder.
168      * 
169      * @return SAML 2 conditions builder
170      */
171     public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
172         return conditionsBuilder;
173     }
174
175     /**
176      * Convenience method for getting the SAML 2 Issuer builder.
177      * 
178      * @return SAML 2 Issuer builder
179      */
180     public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
181         return issuerBuilder;
182     }
183
184     /**
185      * Convenience method for getting the SAML 2 proxy restriction builder.
186      * 
187      * @return SAML 2 proxy restriction builder
188      */
189     public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
190         return proxyRestrictionBuilder;
191     }
192
193     /**
194      * Convenience method for getting the SAML 2 response builder.
195      * 
196      * @return SAML 2 response builder
197      */
198     public SAMLObjectBuilder<Response> getResponseBuilder() {
199         return responseBuilder;
200     }
201
202     /**
203      * Convenience method for getting the Signature builder.
204      * 
205      * @return signature builder
206      */
207     public XMLObjectBuilder<Signature> getSignatureBuilder() {
208         return signatureBuilder;
209     }
210
211     /**
212      * Convenience method for getting the SAML 2 status builder.
213      * 
214      * @return SAML 2 status builder
215      */
216     public SAMLObjectBuilder<Status> getStatusBuilder() {
217         return statusBuilder;
218     }
219
220     /**
221      * Convenience method for getting the SAML 2 status code builder.
222      * 
223      * @return SAML 2 status code builder
224      */
225     public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
226         return statusCodeBuilder;
227     }
228
229     /**
230      * Convenience method for getting the SAML 2 status message builder.
231      * 
232      * @return SAML 2 status message builder
233      */
234     public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
235         return statusMessageBuilder;
236     }
237
238     /**
239      * Convenience method for getting the SAML 2 subject builder.
240      * 
241      * @return SAML 2 subject builder
242      */
243     public SAMLObjectBuilder<Subject> getSubjectBuilder() {
244         return subjectBuilder;
245     }
246
247     /**
248      * Populates the response's id, in response to, issue instant, version, and issuer properties.
249      * 
250      * @param response the response to populate
251      * @param issueInstant timestamp to use as the issue instant for the response
252      * @param request the request that the response is for
253      * @param rpConfig the relying party configuration for the request
254      */
255     protected void populateStatusResponse(StatusResponseType response, DateTime issueInstant,
256             RequestAbstractType request, RelyingPartyConfiguration rpConfig) {
257
258         response.setID(getIdGenerator().generateIdentifier());
259         response.setInResponseTo(request.getID());
260         response.setIssueInstant(issueInstant);
261         response.setVersion(SAMLVersion.VERSION_20);
262         response.setIssuer(buildEntityIssuer(rpConfig));
263     }
264
265     /**
266      * Build a status message, with an optional second-level failure message.
267      * 
268      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
269      * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
270      *            null, no second-level Status element will be set.
271      * @param secondLevelFailureMessage An optional second-level failure message.
272      * 
273      * @return a Status object.
274      */
275     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
276
277         Status status = statusBuilder.buildObject();
278         
279         StatusCode statusCode = statusCodeBuilder.buildObject();
280         statusCode.setValue(DatatypeHelper.safeTrimOrNullString(topLevelCode));
281         status.setStatusCode(statusCode);
282         
283         if (secondLevelCode != null) {
284             StatusCode secondLevelStatusCode = statusCodeBuilder.buildObject();
285             secondLevelStatusCode.setValue(DatatypeHelper.safeTrimOrNullString(secondLevelCode));
286             statusCode.setStatusCode(secondLevelStatusCode);
287         }
288
289         if (secondLevelFailureMessage != null) {
290             StatusMessage msg = statusMessageBuilder.buildObject();
291             msg.setMessage(secondLevelFailureMessage);
292             status.setStatusMessage(msg);
293         }
294
295         return status;
296     }
297
298     /**
299      * Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
300      * 
301      * @param issueInstant time to use as assertion issue instant
302      * @param rpConfig the relying party configuration
303      * @param profileConfig current profile configuration
304      * 
305      * @return the built assertion
306      */
307     protected Assertion buildAssertion(final DateTime issueInstant, final RelyingPartyConfiguration rpConfig,
308             final AbstractSAML2ProfileConfiguration profileConfig) {
309
310         Assertion assertion = assertionBuilder.buildObject();
311         assertion.setID(getIdGenerator().generateIdentifier());
312         assertion.setIssueInstant(issueInstant);
313         assertion.setVersion(SAMLVersion.VERSION_20);
314         assertion.setIssuer(buildEntityIssuer(rpConfig));
315         // TODO assertion.setSubject(buildSubject());
316
317         Conditions conditions = buildConditions(issueInstant, profileConfig);
318         assertion.setConditions(conditions);
319
320         return assertion;
321     }
322
323     /**
324      * Builds an entity type Issuer populated with the correct provider Id for this relying party configuration.
325      * 
326      * @param rpConfig the relying party configuration
327      * 
328      * @return the built Issuer
329      */
330     protected Issuer buildEntityIssuer(final RelyingPartyConfiguration rpConfig) {
331
332         Issuer issuer = getIssuerBuilder().buildObject();
333         issuer.setFormat(Issuer.ENTITY);
334         issuer.setValue(rpConfig.getProviderId());
335
336         return issuer;
337     }
338
339     /**
340      * Builds the SAML subject for the user for the service provider.
341      * 
342      * @return SAML subject for the user for the service provider
343      * 
344      * @throws EncryptionException thrown if there is a problem encryption the subject's NameID
345      */
346     protected Subject buildSubject() throws EncryptionException {
347         // TODO
348         return null;
349     }
350
351     /**
352      * Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
353      * restrictions, and proxy restrictions.
354      * 
355      * @param issueInstant timestamp the assertion was created
356      * @param profileConfig current profile configuration
357      * 
358      * @return constructed conditions
359      */
360     protected Conditions buildConditions(final DateTime issueInstant,
361             final AbstractSAML2ProfileConfiguration profileConfig) {
362
363         Conditions conditions = conditionsBuilder.buildObject();
364         conditions.setNotBefore(issueInstant);
365         conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
366
367         Collection<String> audiences;
368
369         // add audience restrictions
370         audiences = profileConfig.getAssertionAudiences();
371         if (audiences != null && audiences.size() > 0) {
372             AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
373             for (String audienceUri : audiences) {
374                 Audience audience = audienceBuilder.buildObject();
375                 audience.setAudienceURI(audienceUri);
376                 audienceRestriction.getAudiences().add(audience);
377             }
378             conditions.getAudienceRestrictions().add(audienceRestriction);
379         }
380
381         // add proxy restrictions
382         audiences = profileConfig.getProxyAudiences();
383         if (audiences != null && audiences.size() > 0) {
384             ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
385             Audience audience;
386             for (String audienceUri : audiences) {
387                 audience = audienceBuilder.buildObject();
388                 audience.setAudienceURI(audienceUri);
389                 proxyRestriction.getAudiences().add(audience);
390             }
391
392             proxyRestriction.setProxyCount(profileConfig.getProxyCount());
393             conditions.getConditions().add(proxyRestriction);
394         }
395
396         return conditions;
397     }
398
399     /**
400      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
401      * signing credentials.
402      * 
403      * @param assertion assertion to sign
404      * @param rpConfig relying party configuration
405      * @param profileConfig current profile configuration
406      */
407     protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
408             AbstractSAML2ProfileConfiguration profileConfig) {
409         if (!profileConfig.getSignAssertions()) {
410             return;
411         }
412
413         Credential signatureCredential = profileConfig.getSigningCredential();
414         if (signatureCredential == null) {
415             signatureCredential = rpConfig.getDefaultSigningCredential();
416         }
417
418         if (signatureCredential == null) {
419             return;
420         }
421
422         SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
423         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
424         signature.getContentReferences().add(contentRef);
425         assertion.setSignature(signature);
426
427         Signer.signObject(signature);
428     }
429
430     // TODO encryption support
431 }