diff options
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java')
-rw-r--r-- | eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java | 449 |
1 files changed, 222 insertions, 227 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java index 79578788..c679de20 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/PostBinding.java @@ -1,242 +1,237 @@ -/******************************************************************************* - * 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. +/* + * 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: + * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ + * 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 org.apache.commons.lang3.StringUtils; -import org.opensaml.common.SAMLObject; -import org.opensaml.common.binding.BasicSAMLMessageContext; -import org.opensaml.common.binding.decoding.URIComparator; -import org.opensaml.common.xml.SAMLConstants; -import org.opensaml.saml2.binding.decoding.HTTPPostDecoder; -import org.opensaml.saml2.core.RequestAbstractType; -import org.opensaml.saml2.core.StatusResponseType; -import org.opensaml.saml2.metadata.IDPSSODescriptor; -import org.opensaml.saml2.metadata.SPSSODescriptor; -import org.opensaml.saml2.metadata.SingleSignOnService; -import org.opensaml.saml2.metadata.impl.SingleSignOnServiceBuilder; -import org.opensaml.saml2.metadata.provider.MetadataProvider; -import org.opensaml.ws.message.decoder.MessageDecodingException; -import org.opensaml.ws.message.encoder.MessageEncodingException; -import org.opensaml.ws.security.SecurityPolicyResolver; -import org.opensaml.ws.security.provider.BasicSecurityPolicy; -import org.opensaml.ws.security.provider.StaticSecurityPolicyResolver; -import org.opensaml.ws.transport.http.HttpServletRequestAdapter; -import org.opensaml.ws.transport.http.HttpServletResponseAdapter; -import org.opensaml.xml.parse.BasicParserPool; -import org.opensaml.xml.security.SecurityException; -import org.opensaml.xml.security.credential.Credential; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; +import javax.xml.namespace.QName; 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.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.core.impl.gui.velocity.VelocityProvider; -import at.gv.egiz.eaaf.modules.pvp2.PVPConstants; +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.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.HTTPPostEncoderWithOwnTemplate; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EAAFDefaultSAML2Bootstrap; -import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory; -import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PVPSignedRequestPolicyRule; - -@Service("PVPPOSTBinding") -public class PostBinding implements IDecoder, IEncoder { - private static final Logger log = LoggerFactory.getLogger(PostBinding.class); - - @Autowired(required=true) IConfiguration authConfig; - @Autowired(required=true) IVelocityGuiFormBuilder guiBuilder; - @Autowired(required=true) IGUIBuilderConfigurationFactory guiConfigFactory; - - @Override - public void encodeRequest(HttpServletRequest req, HttpServletResponse resp, - RequestAbstractType request, String targetLocation, String relayState, Credential credentials, IRequest pendingReq) - throws MessageEncodingException, SecurityException { - - try { - //load default PVP security configurations - EAAFDefaultSAML2Bootstrap.initializeDefaultPVPConfiguration(); - - //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, - VelocityProvider.getClassPathVelocityEngine()); - - //set OpenSAML2 process parameter into binding context dao - final HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter( - resp, true); - final BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject> context = new BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject>(); - final SingleSignOnService service = new SingleSignOnServiceBuilder().buildObject(); - service.setBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"); - service.setLocation(targetLocation);; - - context.setOutboundSAMLMessageSigningCredential(credentials); - context.setPeerEntityEndpoint(service); - context.setOutboundSAMLMessage(request); - context.setOutboundMessageTransport(responseAdapter); - context.setRelayState(relayState); - - encoder.encode(context); - - } catch (final Exception e) { - log.warn("Can not encode SAML2 request", e); - throw new SecurityException(e); - - } - } - - @Override - public void encodeRespone(HttpServletRequest req, HttpServletResponse resp, - StatusResponseType response, String targetLocation, String relayState, Credential credentials, IRequest pendingReq) - throws MessageEncodingException, SecurityException { - - try { - //load default PVP security configurations - EAAFDefaultSAML2Bootstrap.initializeDefaultPVPConfiguration(); - - 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, - VelocityProvider.getClassPathVelocityEngine()); - - //set OpenSAML2 process parameter into binding context dao - final HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter( - resp, true); - final BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject> context = new BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject>(); - final SingleSignOnService service = new SingleSignOnServiceBuilder() - .buildObject(); - service.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); - service.setLocation(targetLocation); - context.setOutboundSAMLMessageSigningCredential(credentials); - context.setPeerEntityEndpoint(service); - // context.setOutboundMessage(authReq); - context.setOutboundSAMLMessage(response); - context.setOutboundMessageTransport(responseAdapter); - context.setRelayState(relayState); - - encoder.encode(context); - - } catch (final Exception e) { - log.warn("Can not encode SAML2 response", e); - throw new SecurityException(e); - - } - } - - @Override - public InboundMessageInterface decode(HttpServletRequest req, - HttpServletResponse resp, MetadataProvider metadataProvider, boolean isSPEndPoint, URIComparator comparator) throws MessageDecodingException, - SecurityException { - - final HTTPPostDecoder decode = new HTTPPostDecoder(new BasicParserPool()); - final BasicSAMLMessageContext<SAMLObject, ?, ?> messageContext = new BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject>(); - messageContext - .setInboundMessageTransport(new HttpServletRequestAdapter(req)); - //set metadata descriptor type - if (isSPEndPoint) { - messageContext.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); - decode.setURIComparator(comparator); - - } else { - messageContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); - decode.setURIComparator(comparator); - } - - messageContext.setMetadataProvider(metadataProvider); - - //set security policy context - final BasicSecurityPolicy policy = new BasicSecurityPolicy(); - policy.getPolicyRules().add( - new PVPSignedRequestPolicyRule(metadataProvider, - TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider), - messageContext.getPeerEntityRole())); - final SecurityPolicyResolver secResolver = new StaticSecurityPolicyResolver(policy); - messageContext.setSecurityPolicyResolver(secResolver); - - decode.decode(messageContext); - - InboundMessage msg = null; - if (messageContext.getInboundMessage() instanceof RequestAbstractType) { - final RequestAbstractType inboundMessage = (RequestAbstractType) messageContext - .getInboundMessage(); - msg = new PVPSProfileRequest(inboundMessage, getSAML2BindingName()); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else if (messageContext.getInboundMessage() instanceof StatusResponseType){ - final StatusResponseType inboundMessage = (StatusResponseType) messageContext.getInboundMessage(); - msg = new PVPSProfileResponse(inboundMessage); - msg.setEntityID(inboundMessage.getIssuer().getValue()); - - } else - //create empty container if request type is unknown - msg = new InboundMessage(); - - if (messageContext.getPeerEntityMetadata() != null) - msg.setEntityID(messageContext.getPeerEntityMetadata().getEntityID()); - - else { - if (StringUtils.isEmpty(msg.getEntityID())) - log.info("No Metadata found for OA with EntityID " + messageContext.getInboundMessageIssuer()); - } - - - msg.setVerified(true); - msg.setRelayState(messageContext.getRelayState()); - - return msg; - } - - @Override - public boolean handleDecode(String action, HttpServletRequest req) { - return (req.getMethod().equals("POST") && action.equals(PVPConstants.POST)); - } - - @Override - public String getSAML2BindingName() { - return SAMLConstants.SAML2_POST_BINDING_URI; - } +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.opensaml.EaafHttpPostDecoder; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.HttpPostEncoderWithOwnTemplate; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafSamlProtocolMessageXmlSignatureSecurityHandler; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.PvpSamlMessageHandlerChain; + +import org.opensaml.messaging.context.MessageContext; +import org.opensaml.saml.common.SAMLObject; +import org.opensaml.saml.common.binding.SAMLBindingSupport; +import org.opensaml.saml.common.binding.impl.CheckMessageVersionHandler; +import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHandler; +import org.opensaml.saml.common.binding.security.impl.ReceivedEndpointSecurityHandler; +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.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<SAMLObject> 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<SAMLObject> 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, + QName peerEntityRole, final URIComparator comparator) + throws Pvp2Exception { + + final EaafHttpPostDecoder decode = new EaafHttpPostDecoder(req); + final MessageContext<SAMLObject> messageContext = internalMessageDecode(decode, PvpConstants.POST); + + // check if PVP2 AuthnRequest is signed + if (!SAMLBindingSupport.isMessageSigned(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, peerEntityRole); + final PvpSamlMessageHandlerChain messageValidatorChain = + buildMessageValidationChain(req, comparator, metadataProvider); + + log.trace("Message validation (Signature, ...) on binding-level starts ... "); + performMessageValidation(messageValidatorChain, messageContext); + + log.trace("Message validation successful"); + return performMessageDecodePostProcessing(messageContext, true); + + } + + @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; + + } + + private PvpSamlMessageHandlerChain buildMessageValidationChain(HttpServletRequest req, + URIComparator comparator, IPvp2MetadataProvider metadataProvider) { + final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); + + final ReceivedEndpointSecurityHandler endpointSecurityHandler = new ReceivedEndpointSecurityHandler(); + endpointSecurityHandler.setHttpServletRequest(req); + endpointSecurityHandler.setURIComparator(comparator); + + messageValidatorChain.addHandler(new CheckMessageVersionHandler()); + messageValidatorChain.addHandler(endpointSecurityHandler); + messageValidatorChain.addHandler( + new EaafSamlProtocolMessageXmlSignatureSecurityHandler(metadataProvider)); + messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); + + /* + * TODO: maybe we add it in a later version Because: - AuthnRequest replay + * should not be a problem on IDP side - Response replay will be not possible, + * because EAAF PVP implements countermeasure based on one-time tokens for each + * request + * + */ + // final MessageReplaySecurityHandler replaySecurityHandler = new + // MessageReplaySecurityHandler(); + // final StorageService replayCacheStorage = null; + // final ReplayCache replayCache = new ReplayCache(); + // replayCache.setId("Message replay cache"); + // replayCache.setStrict(true); + // replayCache.setStorage(replayCacheStorage); + // replaySecurityHandler.setReplayCache(replayCache ); + // messageValidatorChain.addHandler(replaySecurityHandler); + + return messageValidatorChain; + + } } |