package org.pac4j.saml.sso.impl; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import org.apache.velocity.app.VelocityEngine; import org.opensaml.messaging.encoder.MessageEncoder; import org.opensaml.messaging.encoder.MessageEncodingException; import org.opensaml.saml.common.binding.impl.SAMLOutboundDestinationHandler; import org.opensaml.saml.common.binding.security.impl.EndpointURLSchemeSecurityHandler; import org.opensaml.saml.common.binding.security.impl.SAMLOutboundProtocolMessageSigningHandler; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.core.AuthnRequest; import org.opensaml.saml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml.saml2.metadata.SingleSignOnService; import org.pac4j.saml.context.SAML2MessageContext; import org.pac4j.saml.crypto.SignatureSigningParametersProvider; import org.pac4j.saml.exceptions.SAMLException; import org.pac4j.saml.sso.SAML2MessageSender; import org.pac4j.saml.storage.SAMLMessageStorage; import org.pac4j.saml.transport.Pac4jHTTPPostEncoder; import org.pac4j.saml.transport.Pac4jHTTPRedirectDeflateEncoder; import org.pac4j.saml.transport.Pac4jSAMLResponse; import org.pac4j.saml.util.VelocityEngineFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Misagh Moayyed */ public class SAML2WebSSOMessageSender implements SAML2MessageSender<AuthnRequest> { private final static Logger logger = LoggerFactory.getLogger(SAML2WebSSOProfileHandler.class); private final SignatureSigningParametersProvider signatureSigningParametersProvider; private final String destinationBindingType; private final boolean forceSignRedirectBindingAuthnRequest; public SAML2WebSSOMessageSender(final SignatureSigningParametersProvider signatureSigningParametersProvider, final String destinationBindingType, final boolean forceSignRedirectBindingAuthnRequest) { this.signatureSigningParametersProvider = signatureSigningParametersProvider; this.destinationBindingType = destinationBindingType; this.forceSignRedirectBindingAuthnRequest = forceSignRedirectBindingAuthnRequest; } @Override public void sendMessage(final SAML2MessageContext context, final AuthnRequest authnRequest, final Object relayState) { final SPSSODescriptor spDescriptor = context.getSPSSODescriptor(); final IDPSSODescriptor idpssoDescriptor = context.getIDPSSODescriptor(); final SingleSignOnService ssoService = context.getIDPSingleSignOnService(destinationBindingType); final AssertionConsumerService acsService = context.getSPAssertionConsumerService(); final MessageEncoder encoder = getMessageEncoder(context); final SAML2MessageContext outboundContext = new SAML2MessageContext(context); outboundContext.getProfileRequestContext().setProfileId(context.getProfileRequestContext().getProfileId()); outboundContext.getProfileRequestContext().setInboundMessageContext( context.getProfileRequestContext().getInboundMessageContext()); outboundContext.getProfileRequestContext().setOutboundMessageContext( context.getProfileRequestContext().getOutboundMessageContext()); outboundContext.setMessage(authnRequest); outboundContext.getSAMLEndpointContext().setEndpoint(acsService); outboundContext.getSAMLPeerEndpointContext().setEndpoint(ssoService); outboundContext.getSAMLPeerEntityContext().setRole(context.getSAMLPeerEntityContext().getRole()); outboundContext.getSAMLPeerEntityContext().setEntityId(context.getSAMLPeerEntityContext().getEntityId()); outboundContext.getSAMLProtocolContext().setProtocol(context.getSAMLProtocolContext().getProtocol()); outboundContext.getSecurityParametersContext() .setSignatureSigningParameters(this.signatureSigningParametersProvider.build(spDescriptor)); if (relayState != null) { outboundContext.getSAMLBindingContext().setRelayState(relayState.toString()); } try { invokeOutboundMessageHandlers(spDescriptor, idpssoDescriptor, outboundContext); encoder.setMessageContext(outboundContext); encoder.initialize(); encoder.prepareContext(); encoder.encode(); final SAMLMessageStorage messageStorage = context.getSAMLMessageStorage(); if (messageStorage != null) { messageStorage.storeMessage(authnRequest.getID(), authnRequest); } } catch (final MessageEncodingException e) { throw new SAMLException("Error encoding saml message", e); } catch (final ComponentInitializationException e) { throw new SAMLException("Error initializing saml encoder", e); } } protected final void invokeOutboundMessageHandlers(final SPSSODescriptor spDescriptor, final IDPSSODescriptor idpssoDescriptor, final SAML2MessageContext outboundContext) { try { final EndpointURLSchemeSecurityHandler handlerEnd = new EndpointURLSchemeSecurityHandler(); handlerEnd.initialize(); handlerEnd.invoke(outboundContext); final SAMLOutboundDestinationHandler handlerDest = new SAMLOutboundDestinationHandler(); handlerDest.initialize(); handlerDest.invoke(outboundContext); if (spDescriptor.isAuthnRequestsSigned()) { final SAMLOutboundProtocolMessageSigningHandler handler = new SAMLOutboundProtocolMessageSigningHandler(); handler.invoke(outboundContext); } else if (idpssoDescriptor.getWantAuthnRequestsSigned()) { logger.warn("IdP wants authn requests signed, it will perhaps reject your authn requests unless you provide a keystore"); } } catch (final Exception e) { throw new SAMLException(e); } } /** * Build the WebSSO handler for sending and receiving SAML2 messages. * * @param ctx the ctx * @return the encoder instance */ private MessageEncoder getMessageEncoder(final SAML2MessageContext ctx) { final Pac4jSAMLResponse adapter = ctx.getProfileRequestContextOutboundMessageTransportResponse(); if (SAMLConstants.SAML2_POST_BINDING_URI.equals(destinationBindingType)) { final VelocityEngine velocityEngine = VelocityEngineFactory.getEngine(); final Pac4jHTTPPostEncoder encoder = new Pac4jHTTPPostEncoder(adapter); encoder.setVelocityEngine(velocityEngine); return encoder; } if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(destinationBindingType)) { return new Pac4jHTTPRedirectDeflateEncoder(adapter, forceSignRedirectBindingAuthnRequest); } throw new UnsupportedOperationException("Binding type - " + destinationBindingType + " is not supported"); } }