2 * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
19 import java.util.Collection;
21 import org.joda.time.DateTime;
23 import org.opensaml.common.SAMLObjectBuilder;
24 import org.opensaml.common.SAMLVersion;
25 import org.opensaml.common.impl.SAMLObjectContentReference;
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;
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;
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;
54 * Common implementation details for profile handlers.
56 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
58 /** SAML Version for this profile handler. */
59 public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
61 /** URI for the SAML 2 protocol. */
62 public static final String SAML20_PROTOCOL_URI = "urn:oasis:names:tc:SAML:2.0:protocol";
64 /** For building response. */
65 private SAMLObjectBuilder<Response> responseBuilder;
67 /** For building status. */
68 private SAMLObjectBuilder<Status> statusBuilder;
70 /** For building statuscode. */
71 private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
73 /** For building StatusMessages. */
74 private SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
76 /** For building assertion. */
77 private SAMLObjectBuilder<Assertion> assertionBuilder;
79 /** For building issuer. */
80 private SAMLObjectBuilder<Issuer> issuerBuilder;
82 /** For building subject. */
83 private SAMLObjectBuilder<Subject> subjectBuilder;
85 /** For building conditions. */
86 private SAMLObjectBuilder<Conditions> conditionsBuilder;
88 /** For building audience restriction. */
89 private SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
91 /** For building proxy retrictions. */
92 private SAMLObjectBuilder<ProxyRestriction> proxyRestrictionBuilder;
94 /** For building audience. */
95 private SAMLObjectBuilder<Audience> audienceBuilder;
97 /** For building advice. */
98 private SAMLObjectBuilder<Advice> adviceBuilder;
100 /** For building signature. */
101 private XMLObjectBuilder<Signature> signatureBuilder;
104 @SuppressWarnings("unchecked")
105 protected AbstractSAML2ProfileHandler() {
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);
125 * Convenience method for getting the SAML 2 advice builder.
127 * @return SAML 2 advice builder
129 public SAMLObjectBuilder<Advice> getAdviceBuilder() {
130 return adviceBuilder;
134 * Convenience method for getting the SAML 2 assertion builder.
136 * @return SAML 2 assertion builder
138 public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
139 return assertionBuilder;
143 * Convenience method for getting the SAML 2 audience builder.
145 * @return SAML 2 audience builder
147 public SAMLObjectBuilder<Audience> getAudienceBuilder() {
148 return audienceBuilder;
152 * Convenience method for getting the SAML 2 audience restriction builder.
154 * @return SAML 2 audience restriction builder
156 public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
157 return audienceRestrictionBuilder;
161 * Convenience method for getting the SAML 2 conditions builder.
163 * @return SAML 2 conditions builder
165 public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
166 return conditionsBuilder;
170 * Convenience method for getting the SAML 2 Issuer builder.
172 * @return SAML 2 Issuer builder
174 public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
175 return issuerBuilder;
179 * Convenience method for getting the SAML 2 proxy restriction builder.
181 * @return SAML 2 proxy restriction builder
183 public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
184 return proxyRestrictionBuilder;
188 * Convenience method for getting the SAML 2 response builder.
190 * @return SAML 2 response builder
192 public SAMLObjectBuilder<Response> getResponseBuilder() {
193 return responseBuilder;
197 * Convenience method for getting the Signature builder.
199 * @return signature builder
201 public XMLObjectBuilder<Signature> getSignatureBuilder() {
202 return signatureBuilder;
206 * Convenience method for getting the SAML 2 status builder.
208 * @return SAML 2 status builder
210 public SAMLObjectBuilder<Status> getStatusBuilder() {
211 return statusBuilder;
215 * Convenience method for getting the SAML 2 status code builder.
217 * @return SAML 2 status code builder
219 public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
220 return statusCodeBuilder;
224 * Convenience method for getting the SAML 2 status message builder.
226 * @return SAML 2 status message builder
228 public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
229 return statusMessageBuilder;
233 * Convenience method for getting the SAML 2 subject builder.
235 * @return SAML 2 subject builder
237 public SAMLObjectBuilder<Subject> getSubjectBuilder() {
238 return subjectBuilder;
242 * Populates the response's id, in response to, issue instant, version, and issuer properties.
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
249 protected void populateStatusResponse(StatusResponseType response, DateTime issueInstant,
250 RequestAbstractType request, RelyingPartyConfiguration rpConfig) {
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));
260 * Build a status message, with an optional second-level failure message.
262 * @param topLevelCode
263 * The top-level status code. Should be from saml-core-2.0-os,
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.
272 * @return a Status object.
274 protected Status buildStatus(String topLevelCode, String secondLevelCode,
275 String secondLevelFailureMessage) {
277 Status status = statusBuilder.buildObject();
278 StatusCode statusCode = statusCodeBuilder.buildObject();
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);
287 if (secondLevelFailureMessage != null) {
288 StatusMessage msg = statusMessageBuilder.buildObject();
289 msg.setMessage(secondLevelFailureMessage);
290 status.setStatusMessage(msg);
297 * Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
299 * @param issueInstant time to use as assertion issue instant
300 * @param rpConfig the relying party configuration
301 * @param profileConfig current profile configuration
303 * @return the built assertion
305 protected Assertion buildAssertion(final DateTime issueInstant, final RelyingPartyConfiguration rpConfig,
306 final AbstractSAML2ProfileConfiguration profileConfig) {
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());
315 Conditions conditions = buildConditions(issueInstant, profileConfig);
316 assertion.setConditions(conditions);
322 * Builds an entity type Issuer populated with the correct provider Id for this relying party configuration.
324 * @param rpConfig the relying party configuration
326 * @return the built Issuer
328 protected Issuer buildEntityIssuer(final RelyingPartyConfiguration rpConfig) {
330 Issuer issuer = getIssuerBuilder().buildObject();
331 issuer.setFormat(Issuer.ENTITY);
332 issuer.setValue(rpConfig.getProviderId());
338 * Builds the SAML subject for the user for the service provider.
340 * @return SAML subject for the user for the service provider
342 * @throws EncryptionException thrown if there is a problem encryption the subject's NameID
344 protected Subject buildSubject() throws EncryptionException {
350 * Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
351 * restrictions, and proxy restrictions.
353 * @param issueInstant timestamp the assertion was created
354 * @param profileConfig current profile configuration
356 * @return constructed conditions
358 protected Conditions buildConditions(final DateTime issueInstant, final AbstractSAML2ProfileConfiguration profileConfig) {
360 Conditions conditions = conditionsBuilder.buildObject();
361 conditions.setNotBefore(issueInstant);
362 conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
364 Collection<String> audiences;
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);
375 conditions.getAudienceRestrictions().add(audienceRestriction);
378 // add proxy restrictions
379 audiences = profileConfig.getProxyAudiences();
380 if (audiences != null && audiences.size() > 0) {
381 ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
383 for (String audienceUri : audiences) {
384 audience = audienceBuilder.buildObject();
385 audience.setAudienceURI(audienceUri);
386 proxyRestriction.getAudiences().add(audience);
389 proxyRestriction.setProxyCount(profileConfig.getProxyCount());
390 conditions.getConditions().add(proxyRestriction);
397 * Signs the given assertion if either the current profile configuration or the relying party configuration contains
398 * signing credentials.
400 * @param assertion assertion to sign
401 * @param rpConfig relying party configuration
402 * @param profileConfig current profile configuration
404 protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
405 AbstractSAML2ProfileConfiguration profileConfig) {
406 if (!profileConfig.getSignAssertions()) {
410 Credential signatureCredential = profileConfig.getSigningCredential();
411 if (signatureCredential == null) {
412 signatureCredential = rpConfig.getDefaultSigningCredential();
415 if (signatureCredential == null) {
419 SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
420 Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
421 signature.getContentReferences().add(contentRef);
422 assertion.setSignature(signature);
424 Signer.signObject(signature);
427 protected void signResponse(StatusResponseType response, RelyingPartyConfiguration rpConfig, AbstractSAML2ProfileConfiguration profileConfig){
428 if (!profileConfig.getSignResponses()) {
432 Credential signatureCredential = profileConfig.getSigningCredential();
433 if (signatureCredential == null) {
434 signatureCredential = rpConfig.getDefaultSigningCredential();
437 if (signatureCredential == null) {
441 SAMLObjectContentReference contentRef = new SAMLObjectContentReference(response);
442 Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
443 signature.getContentReferences().add(contentRef);
444 response.setSignature(signature);
446 Signer.signObject(signature);
449 // TODO encryption support