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;
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;
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;
51 * Common implementation details for profile handlers.
53 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
55 /** For building response. */
56 private SAMLObjectBuilder<Response> responseBuilder;
58 /** For building status. */
59 private SAMLObjectBuilder<Status> statusBuilder;
61 /** For building statuscode. */
62 private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
64 /** For building StatusMessages. */
65 private SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
67 /** For building assertion. */
68 private SAMLObjectBuilder<Assertion> assertionBuilder;
70 /** For building issuer. */
71 private SAMLObjectBuilder<Issuer> issuerBuilder;
73 /** For building subject. */
74 private SAMLObjectBuilder<Subject> subjectBuilder;
76 /** For building conditions. */
77 private SAMLObjectBuilder<Conditions> conditionsBuilder;
79 /** For building audience restriction. */
80 private SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder;
82 /** For building proxy retrictions. */
83 private SAMLObjectBuilder<ProxyRestriction> proxyRestrictionBuilder;
85 /** For building audience. */
86 private SAMLObjectBuilder<Audience> audienceBuilder;
88 /** For building advice. */
89 private SAMLObjectBuilder<Advice> adviceBuilder;
91 /** For building signature. */
92 private XMLObjectBuilder<Signature> signatureBuilder;
95 @SuppressWarnings("unchecked")
96 protected AbstractSAML2ProfileHandler() {
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);
121 * Convenience method for getting the SAML 2 advice builder.
123 * @return SAML 2 advice builder
125 public SAMLObjectBuilder<Advice> getAdviceBuilder() {
126 return adviceBuilder;
130 * Convenience method for getting the SAML 2 assertion builder.
132 * @return SAML 2 assertion builder
134 public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
135 return assertionBuilder;
139 * Convenience method for getting the SAML 2 audience builder.
141 * @return SAML 2 audience builder
143 public SAMLObjectBuilder<Audience> getAudienceBuilder() {
144 return audienceBuilder;
148 * Convenience method for getting the SAML 2 audience restriction builder.
150 * @return SAML 2 audience restriction builder
152 public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
153 return audienceRestrictionBuilder;
157 * Convenience method for getting the SAML 2 conditions builder.
159 * @return SAML 2 conditions builder
161 public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
162 return conditionsBuilder;
166 * Convenience method for getting the SAML 2 Issuer builder.
168 * @return SAML 2 Issuer builder
170 public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
171 return issuerBuilder;
175 * Convenience method for getting the SAML 2 proxy restriction builder.
177 * @return SAML 2 proxy restriction builder
179 public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
180 return proxyRestrictionBuilder;
184 * Convenience method for getting the SAML 2 response builder.
186 * @return SAML 2 response builder
188 public SAMLObjectBuilder<Response> getResponseBuilder() {
189 return responseBuilder;
193 * Convenience method for getting the Signature builder.
195 * @return signature builder
197 public XMLObjectBuilder<Signature> getSignatureBuilder() {
198 return signatureBuilder;
202 * Convenience method for getting the SAML 2 status builder.
204 * @return SAML 2 status builder
206 public SAMLObjectBuilder<Status> getStatusBuilder() {
207 return statusBuilder;
211 * Convenience method for getting the SAML 2 status code builder.
213 * @return SAML 2 status code builder
215 public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
216 return statusCodeBuilder;
220 * Convenience method for getting the SAML 2 status message builder.
222 * @return SAML 2 status message builder
224 public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
225 return statusMessageBuilder;
229 * Convenience method for getting the SAML 2 subject builder.
231 * @return SAML 2 subject builder
233 public SAMLObjectBuilder<Subject> getSubjectBuilder() {
234 return subjectBuilder;
238 * Populates the response's id, in response to, issue instant, version, and issuer properties.
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
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));
255 * Builds a {@link Status} object populated with the given code and message.
257 * @param statusCode status code or null
258 * @param statusMessage status message or null
260 * @return built status object
262 protected Status buildStatus(String statusCode, String statusMessage) {
263 Status status = getStatusBuilder().buildObject();
265 String trimmedCode = DatatypeHelper.safeTrimOrNullString(statusCode);
266 if (trimmedCode != null) {
267 StatusCode code = getStatusCodeBuilder().buildObject();
268 code.setValue(trimmedCode);
269 status.setStatusCode(code);
272 String trimmedMessage = DatatypeHelper.safeTrimOrNullString(statusMessage);
273 if (trimmedMessage != null) {
274 StatusMessage message = getStatusMessageBuilder().buildObject();
275 message.setMessage(trimmedMessage);
276 status.setStatusMessage(message);
283 * Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
285 * @param issueInstant time to use as assertion issue instant
286 * @param rpConfig the relying party configuration
287 * @param profileConfig current profile configuration
289 * @return the built assertion
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());
300 Conditions conditions = buildConditions(issueInstant, profileConfig);
301 assertion.setConditions(conditions);
307 * Builds an entity type Issuer populated with the correct provider Id for this relying party configuration.
309 * @param rpConfig the relying party configuration
311 * @return the built Issuer
313 protected Issuer buildEntityIssuer(RelyingPartyConfiguration rpConfig) {
314 Issuer issuer = getIssuerBuilder().buildObject();
315 issuer.setFormat(Issuer.ENTITY);
316 issuer.setValue(rpConfig.getProviderId());
322 * Builds the SAML subject for the user for the service provider.
324 * @return SAML subject for the user for the service provider
326 * @throws EncryptionException thrown if there is a problem encryption the subject's NameID
328 protected Subject buildSubject() throws EncryptionException {
334 * Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
335 * restrictions, and proxy restrictions.
337 * @param issueInstant timestamp the assertion was created
338 * @param profileConfig current profile configuration
340 * @return constructed conditions
342 private Conditions buildConditions(DateTime issueInstant, AbstractSAML2ProfileConfiguration profileConfig) {
343 Conditions conditions = conditionsBuilder.buildObject();
344 conditions.setNotBefore(issueInstant);
345 conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
347 Collection<String> audiences;
349 // add audience restrictions
350 audiences = profileConfig.getAssertionAudiences();
351 if (audiences != null && audiences.size() > 0) {
352 AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
354 for (String audienceUri : audiences) {
355 audience = audienceBuilder.buildObject();
356 audience.setAudienceURI(audienceUri);
357 audienceRestriction.getAudiences().add(audience);
359 conditions.getAudienceRestrictions().add(audienceRestriction);
362 // add proxy restrictions
363 audiences = profileConfig.getProxyAudiences();
364 if (audiences != null && audiences.size() > 0) {
365 ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
367 for (String audienceUri : audiences) {
368 audience = audienceBuilder.buildObject();
369 audience.setAudienceURI(audienceUri);
370 proxyRestriction.getAudiences().add(audience);
373 proxyRestriction.setProxyCount(profileConfig.getProxyCount());
374 conditions.getConditions().add(proxyRestriction);
381 * Signs the given assertion if either the current profile configuration or the relying party configuration contains
382 * signing credentials.
384 * @param assertion assertion to sign
385 * @param rpConfig relying party configuration
386 * @param profileConfig current profile configuration
388 protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
389 AbstractSAML2ProfileConfiguration profileConfig) {
390 if (!profileConfig.getSignAssertions()) {
394 Credential signatureCredential = profileConfig.getSigningCredential();
395 if (signatureCredential == null) {
396 signatureCredential = rpConfig.getDefaultSigningCredential();
399 if (signatureCredential == null) {
403 SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
404 Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
405 signature.getContentReferences().add(contentRef);
406 assertion.setSignature(signature);
408 Signer.signObject(signature);
411 protected void signResponse(StatusResponseType response, RelyingPartyConfiguration rpConfig, AbstractSAML2ProfileConfiguration profileConfig){
412 if (!profileConfig.getSignResponses()) {
416 Credential signatureCredential = profileConfig.getSigningCredential();
417 if (signatureCredential == null) {
418 signatureCredential = rpConfig.getDefaultSigningCredential();
421 if (signatureCredential == null) {
425 SAMLObjectContentReference contentRef = new SAMLObjectContentReference(response);
426 Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
427 signature.getContentReferences().add(contentRef);
428 response.setSignature(signature);
430 Signer.signObject(signature);
433 // TODO encryption support