diff options
Diffstat (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java')
-rw-r--r-- | eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java | 325 |
1 files changed, 167 insertions, 158 deletions
diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java index c70060ad..49e93f0a 100644 --- a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/binding/SoapBinding.java @@ -1,172 +1,181 @@ -/******************************************************************************* - * 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. - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -/******************************************************************************* - *******************************************************************************/ -package at.gv.egiz.eaaf.modules.pvp2.impl.binding; + * 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. +*/ -import java.util.List; +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.encoding.HTTPSOAP11Encoder; -import org.opensaml.saml2.core.RequestAbstractType; -import org.opensaml.saml2.core.StatusResponseType; -import org.opensaml.saml2.metadata.SPSSODescriptor; -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.soap.soap11.Envelope; -import org.opensaml.ws.soap.soap11.decoder.http.HTTPSOAP11Decoder; -import org.opensaml.ws.transport.http.HttpServletRequestAdapter; -import org.opensaml.ws.transport.http.HttpServletResponseAdapter; -import org.opensaml.xml.XMLObject; -import org.opensaml.xml.parse.BasicParserPool; -import org.opensaml.xml.security.SecurityException; -import org.opensaml.xml.security.credential.Credential; -import org.opensaml.xml.signature.SignableXMLObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; +import javax.xml.namespace.QName; import at.gv.egiz.eaaf.core.api.IRequest; -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.exception.AttributQueryException; -import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; -import at.gv.egiz.eaaf.modules.pvp2.impl.message.PVPSProfileRequest; -import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EAAFDefaultSAML2Bootstrap; - -@Service("PVPSOAPBinding") -public class SoapBinding implements IDecoder, IEncoder { - - private static final Logger log = LoggerFactory.getLogger(SoapBinding.class); - public InboundMessageInterface decode(HttpServletRequest req, - HttpServletResponse resp, MetadataProvider metadataProvider, boolean isSPEndPoint, URIComparator comparator) throws MessageDecodingException, - SecurityException, PVP2Exception { - HTTPSOAP11Decoder soapDecoder = new HTTPSOAP11Decoder(new BasicParserPool()); - BasicSAMLMessageContext<SAMLObject, ?, ?> messageContext = - new BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject>(); - messageContext - .setInboundMessageTransport(new HttpServletRequestAdapter( - req)); - messageContext.setMetadataProvider(metadataProvider); - - //TODO: update in a futher version: - // requires a special SignedSOAPRequestPolicyRole because - // messageContext.getInboundMessage() is not directly signed - - //set security context -// BasicSecurityPolicy policy = new BasicSecurityPolicy(); -// policy.getPolicyRules().add( -// new MOAPVPSignedRequestPolicyRule( -// TrustEngineFactory.getSignatureKnownKeysTrustEngine(), -// SPSSODescriptor.DEFAULT_ELEMENT_NAME)); -// SecurityPolicyResolver resolver = new StaticSecurityPolicyResolver( -// policy); -// messageContext.setSecurityPolicyResolver(resolver); - - //decode message - soapDecoder.decode(messageContext); - - Envelope inboundMessage = (Envelope) messageContext - .getInboundMessage(); - - if (inboundMessage.getBody() != null) { - List<XMLObject> xmlElemList = inboundMessage.getBody().getUnknownXMLObjects(); - - if (!xmlElemList.isEmpty()) { - SignableXMLObject attrReq = (SignableXMLObject) xmlElemList.get(0); - PVPSProfileRequest request = new PVPSProfileRequest(attrReq, getSAML2BindingName()); - - if (messageContext.getPeerEntityMetadata() != null) - request.setEntityID(messageContext.getPeerEntityMetadata().getEntityID()); - - else if (attrReq instanceof RequestAbstractType) { - RequestAbstractType attributeRequest = (RequestAbstractType) attrReq; - try { - if (StringUtils.isNotEmpty(attributeRequest.getIssuer().getValue()) && - metadataProvider.getRole( - attributeRequest.getIssuer().getValue(), - SPSSODescriptor.DEFAULT_ELEMENT_NAME) != null) - request.setEntityID(attributeRequest.getIssuer().getValue()); - - } catch (Exception e) { - log.warn("No Metadata found with EntityID " + attributeRequest.getIssuer().getValue()); - } - } - - request.setVerified(false); - return request; - - } - } - - log.error("Receive empty PVP 2.1 attributequery request."); - throw new AttributQueryException("Receive empty PVP 2.1 attributequery request.", null); - } - - public boolean handleDecode(String action, HttpServletRequest req) { - return (req.getMethod().equals("POST") && - (action.equals(PVPConstants.SOAP) || action.equals(PVPConstants.ATTRIBUTEQUERY))); - } - - public void encodeRequest(HttpServletRequest req, HttpServletResponse resp, - RequestAbstractType request, String targetLocation, String relayState, Credential credentials, IRequest pendingReq) - throws MessageEncodingException, SecurityException, PVP2Exception { - - } - - public void encodeRespone(HttpServletRequest req, HttpServletResponse resp, - StatusResponseType response, String targetLocation, String relayState, Credential credentials, IRequest pendingReq) - throws MessageEncodingException, SecurityException, PVP2Exception { - - //load default PVP security configurations - EAAFDefaultSAML2Bootstrap.initializeDefaultPVPConfiguration(); - - HTTPSOAP11Encoder encoder = new HTTPSOAP11Encoder(); - HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter( - resp, true); - BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject> context = new BasicSAMLMessageContext<SAMLObject, SAMLObject, SAMLObject>(); - context.setOutboundSAMLMessageSigningCredential(credentials); - context.setOutboundSAMLMessage(response); - context.setOutboundMessageTransport(responseAdapter); - - encoder.encode(context); - - } - - public String getSAML2BindingName() { - return SAMLConstants.SAML2_SOAP11_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.Pvp2InternalErrorException; +import at.gv.egiz.eaaf.modules.pvp2.exception.SamlBindingException; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; +import at.gv.egiz.eaaf.modules.pvp2.impl.verification.EaafMessageContextInitializationHandler; +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.impl.SAMLProtocolAndRoleHandler; +import org.opensaml.saml.common.binding.impl.SAMLSOAPDecoderBodyHandler; +import org.opensaml.saml.common.binding.security.impl.MessageLifetimeSecurityHandler; +import org.opensaml.saml.common.messaging.SAMLMessageSecuritySupport; +import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.binding.decoding.impl.HTTPSOAP11Decoder; +import org.opensaml.saml.saml2.binding.encoding.impl.HTTPSOAP11Encoder; +import org.opensaml.saml.saml2.core.RequestAbstractType; +import org.opensaml.saml.saml2.core.StatusResponseType; +import org.opensaml.soap.messaging.context.SOAP11Context; + +import lombok.extern.slf4j.Slf4j; +import net.shibboleth.utilities.java.support.component.ComponentInitializationException; +import net.shibboleth.utilities.java.support.net.URIComparator; + +@Slf4j +public class SoapBinding extends AbstractBinding implements IDecoder, IEncoder { + + @Override + public InboundMessageInterface decode(final HttpServletRequest req, + final HttpServletResponse resp, final IPvp2MetadataProvider metadataProvider, + QName peerEntityRole, final URIComparator comparator) + throws Pvp2Exception { + + final HTTPSOAP11Decoder soapDecoder = new HTTPSOAP11Decoder(); + soapDecoder.setHttpServletRequest(req); + + injectMessageHandlerChain(soapDecoder, metadataProvider, peerEntityRole); + + final MessageContext<SAMLObject> messageContext = + internalMessageDecode(soapDecoder, PvpConstants.SOAP); + + // 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); + + } + + return performMessageDecodePostProcessing(messageContext, true); + } + + private void injectMessageHandlerChain(HTTPSOAP11Decoder soapDecoder, + IPvp2MetadataProvider metadataProvider, QName peerEntityRole) throws Pvp2InternalErrorException { + try { + final PvpSamlMessageHandlerChain messageValidatorChain = new PvpSamlMessageHandlerChain(); + messageValidatorChain.addHandler(new EaafMessageContextInitializationHandler(metadataProvider)); + messageValidatorChain.addHandler(new SAMLSOAPDecoderBodyHandler()); + + final SAMLProtocolAndRoleHandler samlProtocolHandler = new SAMLProtocolAndRoleHandler(); + samlProtocolHandler.setProtocol(SAMLConstants.SAML20P_NS); + samlProtocolHandler.setRole(peerEntityRole); + messageValidatorChain.addHandler(samlProtocolHandler); + + messageValidatorChain.addHandler(new CheckMessageVersionHandler()); + messageValidatorChain.addHandler( + new EaafSamlProtocolMessageXmlSignatureSecurityHandler(metadataProvider)); + messageValidatorChain.addHandler(new MessageLifetimeSecurityHandler()); + + messageValidatorChain.initialize(); + + soapDecoder.setBodyHandler(messageValidatorChain); + + } catch (final ComponentInitializationException e) { + log.warn("Internal initialization error. Reason: {}", e.getMessage()); + throw new Pvp2InternalErrorException(e); + + } + + + } + + @Override + public boolean handleDecode(final String action, final HttpServletRequest req) { + return req.getMethod().equals("POST") + && action.equals(PvpConstants.SOAP) || action.equals(PvpConstants.ATTRIBUTEQUERY); + } + + @Override + public void encodeRequest(final HttpServletRequest req, final HttpServletResponse resp, + final RequestAbstractType request, final String targetLocation, final String relayState, + final EaafX509Credential credentials, final IRequest pendingReq) + throws Pvp2Exception { + throw new RuntimeException("Method not supported!!!"); + + } + + @Override + public void encodeResponse(final HttpServletRequest req, final HttpServletResponse resp, + final StatusResponseType response, final String targetLocation, final String relayState, + final EaafX509Credential credentials, final IRequest pendingReq) + throws Pvp2Exception { + + try { + final HTTPSOAP11Encoder encoder = new HTTPSOAP11Encoder(); + encoder.setHttpServletResponse(resp); + + // inject message context + final MessageContext<SAMLObject> messageContext = buildBasicMessageContext(encoder, response); + + //inject SOAP enveloped + final SOAP11Context soap11Context = new SOAP11Context(); + soap11Context.setEnvelope(Saml2Utils.buildSoap11Envelope(response)); + messageContext.addSubcontext(soap11Context); + + // 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 SOAP-Binding response", e); + throw new SamlBindingException("internal.pvp.95", + new Object[] { PvpConstants.SOAP, "encoding", e.getMessage() }, + e); + + } + } + + @Override + public String getSaml2BindingName() { + return SAMLConstants.SAML2_SOAP11_BINDING_URI; + } } |