/* * Copyright 2017 Graz University of Technology EAAF-Core Components has been developed in a * cooperation between EGIZ, A-SIT Plus, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work except in * compliance with the Licence. You may obtain a copy of the Licence at: * https://joinup.ec.europa.eu/news/understanding-eupl-v12 * * Unless required by applicable law or agreed to in writing, software distributed under the Licence * is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the Licence for the specific language governing permissions and limitations under * the Licence. * * This product combines work with different licenses. See the "NOTICE" text file for details on the * various modules and licenses. The "NOTICE" text file is part of the distribution. Any derivative * works that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egiz.eaaf.modules.pvp2.impl.binding; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.gui.IGuiBuilderConfigurationFactory; import at.gv.egiz.eaaf.core.api.gui.IVelocityGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.gui.IVelocityGuiFormBuilder; import at.gv.egiz.eaaf.core.api.idp.IConfiguration; import at.gv.egiz.eaaf.modules.pvp2.PvpConstants; import at.gv.egiz.eaaf.modules.pvp2.api.binding.IDecoder; import at.gv.egiz.eaaf.modules.pvp2.api.binding.IEncoder; import at.gv.egiz.eaaf.modules.pvp2.api.credential.EaafX509Credential; import at.gv.egiz.eaaf.modules.pvp2.api.message.InboundMessageInterface; import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPvp2MetadataProvider; import at.gv.egiz.eaaf.modules.pvp2.exception.InvalidPvpRequestException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2Exception; import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileResponse; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.EaafHttpPostDecoder; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.HttpPostEncoderWithOwnTemplate; import org.opensaml.messaging.context.MessageContext; import org.opensaml.messaging.decoder.MessageDecodingException; import org.opensaml.saml.common.SAMLObject; import org.opensaml.saml.common.binding.SAMLBindingSupport; import org.opensaml.saml.common.messaging.SAMLMessageSecuritySupport; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.core.RequestAbstractType; import org.opensaml.saml.saml2.core.StatusResponseType; import org.springframework.beans.factory.annotation.Autowired; import lombok.extern.slf4j.Slf4j; import net.shibboleth.utilities.java.support.component.ComponentInitializationException; import net.shibboleth.utilities.java.support.net.URIComparator; @Slf4j public class PostBinding extends AbstractBinding implements IDecoder, IEncoder { @Autowired(required = true) IConfiguration authConfig; @Autowired(required = true) IVelocityGuiFormBuilder guiBuilder; @Autowired(required = true) IGuiBuilderConfigurationFactory guiConfigFactory; @Override public void encodeRequest(final HttpServletRequest httpReq, final HttpServletResponse httpResp, final RequestAbstractType request, final String targetLocation, final String relayState, final EaafX509Credential credentials, final IRequest pendingReq) throws Pvp2Exception { try { // initialize POST binding encoder with template decoration final IVelocityGuiBuilderConfiguration guiConfig = guiConfigFactory.getSpSpecificSaml2PostConfiguration(pendingReq, "pvp_postbinding_template.html", authConfig.getConfigurationRootDirectory()); final HttpPostEncoderWithOwnTemplate encoder = new HttpPostEncoderWithOwnTemplate(guiConfig, guiBuilder); encoder.setHttpServletResponse(httpResp); //inject message context final MessageContext messageContext = buildBasicMessageContext(encoder, request); //inject signing context messageContext.addSubcontext(injectSigningInfos(credentials)); //set endpoint url messageContext.addSubcontext(injectEndpointInfos(request, targetLocation)); //set relayState of exists SAMLBindingSupport.setRelayState(messageContext, relayState); //sign SAML2 message SAMLMessageSecuritySupport.signMessage(messageContext); //encode message encoder.initialize(); encoder.encode(); } catch (final Exception e) { log.warn("Can not encode SAML2 Post-Binding request", e); throw new SamlBindingException("internal.pvp.95", new Object[] {PvpConstants.POST, "encoding", e.getMessage()}, e); } } @Override public void encodeResponse(final HttpServletRequest httpReq, final HttpServletResponse httpResp, final StatusResponseType response, final String targetLocation, final String relayState, final EaafX509Credential credentials, final IRequest pendingReq) throws Pvp2Exception { try { log.debug("create SAML POSTBinding response"); // initialize POST binding encoder with template decoration final IVelocityGuiBuilderConfiguration guiConfig = guiConfigFactory.getSpSpecificSaml2PostConfiguration(pendingReq, "pvp_postbinding_template.html", authConfig.getConfigurationRootDirectory()); final HttpPostEncoderWithOwnTemplate encoder = new HttpPostEncoderWithOwnTemplate(guiConfig, guiBuilder); encoder.setHttpServletResponse(httpResp); //inject message context final MessageContext messageContext = buildBasicMessageContext(encoder, response); //inject signing context messageContext.addSubcontext(injectSigningInfos(credentials)); //set endpoint url messageContext.addSubcontext(injectEndpointInfos(response, targetLocation)); //set relayState of exists SAMLBindingSupport.setRelayState(messageContext, relayState); //sign SAML2 message SAMLMessageSecuritySupport.signMessage(messageContext); //encode message encoder.initialize(); encoder.encode(); } catch (final Exception e) { log.warn("Can not encode SAML2 Post-Binding response", e); throw new SamlBindingException("internal.pvp.95", new Object[] {PvpConstants.POST, "encoding", e.getMessage()}, e); } } @Override public InboundMessageInterface decode(final HttpServletRequest req, final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, final boolean isSpEndPoint, final URIComparator comparator) throws Pvp2Exception { //TODO: check, if we should re-implement HTTPPostDecoder to collect the last http parameter!!! final EaafHttpPostDecoder decode = new EaafHttpPostDecoder(); decode.setHttpServletRequest(req); //decode request try { decode.initialize(); decode.decode(); } catch (MessageDecodingException | ComponentInitializationException e) { throw new SamlBindingException("internal.pvp.95", new Object[] {PvpConstants.POST, "decoding", e.getMessage()}, e); } final MessageContext messageContext = decode.getMessageContext(); if (SAMLBindingSupport.isSigningCapableBinding(messageContext)) { log.info("SAML Post-Binding message contains no signature. Message will be rejected"); throw new InvalidPvpRequestException("internal.pvp.02", null); } //inject informations into message context that are required for further processing injectInboundMessageContexts(messageContext, metadataProvider); //TODO: add sig validation!!! InboundMessage msg = null; if (messageContext.getMessage() instanceof RequestAbstractType) { final RequestAbstractType inboundMessage = (RequestAbstractType) messageContext.getMessage(); msg = new PvpSProfileRequest(inboundMessage, getSaml2BindingName()); msg.setEntityID(inboundMessage.getIssuer().getValue()); } else if (messageContext.getMessage() instanceof StatusResponseType) { final StatusResponseType inboundMessage = (StatusResponseType) messageContext.getMessage(); msg = new PvpSProfileResponse(inboundMessage); msg.setEntityID(inboundMessage.getIssuer().getValue()); } else { // create empty container if request type is unknown msg = new InboundMessage(); } msg.setVerified(false); msg.setRelayState(SAMLBindingSupport.getRelayState(messageContext)); return msg; } @Override public boolean handleDecode(final String action, final HttpServletRequest req) { return req.getMethod().equals("POST") && action.equals(PvpConstants.POST); } @Override public String getSaml2BindingName() { return SAMLConstants.SAML2_POST_BINDING_URI; } }