package org.apereo.cas.support.saml.web.idp.profile.builders.response;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.support.saml.OpenSamlConfigBean;
import org.apereo.cas.support.saml.SamlException;
import org.apereo.cas.support.saml.SamlProtocolConstants;
import org.apereo.cas.support.saml.services.SamlRegisteredService;
import org.apereo.cas.support.saml.services.idp.metadata.SamlRegisteredServiceServiceProviderMetadataFacade;
import org.apereo.cas.support.saml.util.AbstractSaml20ObjectBuilder;
import org.apereo.cas.support.saml.web.idp.profile.builders.SamlProfileObjectBuilder;
import org.apereo.cas.support.saml.web.idp.profile.builders.enc.BaseSamlObjectSigner;
import org.apereo.cas.support.saml.web.idp.profile.builders.enc.SamlObjectEncrypter;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.EncryptedAssertion;
import org.opensaml.saml.saml2.core.Issuer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.velocity.VelocityEngineFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* The {@link BaseSamlProfileSamlResponseBuilder} is responsible for
* building the final SAML assertion for the relying party.
*
* @author Misagh Moayyed
* @since 4.2
*/
public abstract class BaseSamlProfileSamlResponseBuilder<T extends XMLObject>
extends AbstractSaml20ObjectBuilder implements SamlProfileObjectBuilder {
private static final long serialVersionUID = -1891703354216174875L;
private static final Logger LOGGER = LoggerFactory.getLogger(BaseSamlProfileSamlResponseBuilder.class);
/**
* The Saml object encoder.
*/
protected BaseSamlObjectSigner samlObjectSigner;
/**
* The Velocity engine factory.
*/
protected VelocityEngineFactory velocityEngineFactory;
@Autowired
private CasConfigurationProperties casProperties;
private final SamlProfileObjectBuilder<Assertion> samlProfileSamlAssertionBuilder;
private final SamlObjectEncrypter samlObjectEncrypter;
public BaseSamlProfileSamlResponseBuilder(final OpenSamlConfigBean openSamlConfigBean,
final BaseSamlObjectSigner samlObjectSigner,
final VelocityEngineFactory velocityEngineFactory,
final SamlProfileObjectBuilder<Assertion> samlProfileSamlAssertionBuilder,
final SamlObjectEncrypter samlObjectEncrypter) {
super(openSamlConfigBean);
this.samlObjectSigner = samlObjectSigner;
this.velocityEngineFactory = velocityEngineFactory;
this.samlProfileSamlAssertionBuilder = samlProfileSamlAssertionBuilder;
this.samlObjectEncrypter = samlObjectEncrypter;
}
@Override
public T build(final AuthnRequest authnRequest,
final HttpServletRequest request,
final HttpServletResponse response,
final org.jasig.cas.client.validation.Assertion casAssertion,
final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final String binding) throws SamlException {
final Assertion assertion = buildSamlAssertion(authnRequest, request, response,
casAssertion, service, adaptor, binding);
final T finalResponse = buildResponse(assertion, casAssertion, authnRequest,
service, adaptor, request, response, binding);
return encodeFinalResponse(request, response, service, adaptor, finalResponse, binding);
}
/**
* Encode final response.
*
* @param request the request
* @param response the response
* @param service the service
* @param adaptor the adaptor
* @param finalResponse the final response
* @param binding the binding
* @return the response
*/
protected T encodeFinalResponse(final HttpServletRequest request,
final HttpServletResponse response,
final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final T finalResponse,
final String binding) {
final String relayState = request.getParameter(SamlProtocolConstants.PARAMETER_SAML_RELAY_STATE);
LOGGER.debug("RelayState is [{}]", relayState);
return encode(service, finalResponse, response, adaptor, relayState, binding);
}
/**
* Build saml assertion assertion.
*
* @param authnRequest the authn request
* @param request the request
* @param response the response
* @param casAssertion the cas assertion
* @param service the service
* @param adaptor the adaptor
* @param binding the binding
* @return the assertion
*/
protected Assertion buildSamlAssertion(final AuthnRequest authnRequest,
final HttpServletRequest request,
final HttpServletResponse response,
final org.jasig.cas.client.validation.Assertion casAssertion,
final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
final String binding) {
return this.samlProfileSamlAssertionBuilder.build(authnRequest, request, response,
casAssertion, service, adaptor, binding);
}
/**
* Build response response.
*
* @param assertion the assertion
* @param casAssertion the cas assertion
* @param authnRequest the authn request
* @param service the service
* @param adaptor the adaptor
* @param request the request
* @param response the response
* @param binding the binding
* @return the response
* @throws SamlException the saml exception
*/
protected abstract T buildResponse(Assertion assertion,
org.jasig.cas.client.validation.Assertion casAssertion,
AuthnRequest authnRequest,
SamlRegisteredService service,
SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
HttpServletRequest request,
HttpServletResponse response,
String binding) throws SamlException;
/**
* Build entity issuer issuer.
*
* @return the issuer
*/
protected Issuer buildEntityIssuer() {
final Issuer issuer = newIssuer(casProperties.getAuthn().getSamlIdp().getEntityId());
issuer.setFormat(Issuer.ENTITY);
return issuer;
}
/**
* Encode the final result into the http response.
*
* @param service the service
* @param samlResponse the saml response
* @param httpResponse the http response; may be null to mute encoding.
* @param adaptor the adaptor
* @param relayState the relay state
* @param binding the binding
* @return the t
* @throws SamlException the saml exception
*/
protected abstract T encode(SamlRegisteredService service,
T samlResponse,
HttpServletResponse httpResponse,
SamlRegisteredServiceServiceProviderMetadataFacade adaptor,
String relayState,
String binding) throws SamlException;
/**
* Encrypt assertion.
*
* @param assertion the assertion
* @param request the request
* @param response the response
* @param service the service
* @param adaptor the adaptor
* @return the saml object
* @throws SamlException the saml exception
*/
protected SAMLObject encryptAssertion(final Assertion assertion,
final HttpServletRequest request, final HttpServletResponse response,
final SamlRegisteredService service,
final SamlRegisteredServiceServiceProviderMetadataFacade adaptor) throws SamlException {
try {
if (service.isEncryptAssertions()) {
LOGGER.info("SAML service [{}] requires assertions to be encrypted", adaptor.getEntityId());
final EncryptedAssertion encryptedAssertion =
this.samlObjectEncrypter.encode(assertion, service, adaptor, response, request);
return encryptedAssertion;
}
LOGGER.info("SAML registered service [{}] does not require assertions to be encrypted", adaptor.getEntityId());
return assertion;
} catch (final Exception e) {
throw new SamlException("Unable to marshall assertion for encryption", e);
}
}
}