diff --git a/phase4-lib/src/main/java/com/helger/phase4/incoming/AS4IncomingHandler.java b/phase4-lib/src/main/java/com/helger/phase4/incoming/AS4IncomingHandler.java index c0c0ff14c..b6731d0eb 100644 --- a/phase4-lib/src/main/java/com/helger/phase4/incoming/AS4IncomingHandler.java +++ b/phase4-lib/src/main/java/com/helger/phase4/incoming/AS4IncomingHandler.java @@ -1072,6 +1072,7 @@ public static IAS4IncomingMessageState processEbmsMessage (@NonNull @WillNotClos { final ErrorList aErrorList = new ErrorList (); aValidator.validatePMode (aPMode, aErrorList, EAS4ProfileValidationMode.USER_MESSAGE); + aValidator.validateSoapMessage (aSoapDocument, aIncomingState.getSoapVersion(), aErrorList); aValidator.validateUserMessage (aEbmsUserMessage, aErrorList); aValidator.validateInitiatorIdentity (aEbmsUserMessage, aIncomingState.getSigningCertificate (), diff --git a/phase4-lib/src/main/java/com/helger/phase4/profile/IAS4ProfileValidator.java b/phase4-lib/src/main/java/com/helger/phase4/profile/IAS4ProfileValidator.java index eaef47e37..85e3d7375 100644 --- a/phase4-lib/src/main/java/com/helger/phase4/profile/IAS4ProfileValidator.java +++ b/phase4-lib/src/main/java/com/helger/phase4/profile/IAS4ProfileValidator.java @@ -19,6 +19,7 @@ import java.security.cert.X509Certificate; import java.util.EnumSet; +import com.helger.phase4.model.ESoapVersion; import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; @@ -29,6 +30,7 @@ import com.helger.phase4.ebms3header.Ebms3UserMessage; import com.helger.phase4.incoming.IAS4IncomingMessageMetadata; import com.helger.phase4.model.pmode.IPMode; +import org.w3c.dom.Document; /** * Generic AS4 profile validator @@ -137,7 +139,20 @@ default void validateInitiatorIdentity (@NonNull final Ebms3UserMessage aUserMsg {} /** - * Validation a UserMessage + * Validation of a SoapDocument + * + * @param aSoapDocument + * The SOAP document to be validated. May not be null. + * @param eSoapVersion + * The SOAP version of the document to be validated. + * @param aErrorList + * The error list to be filled. May not be null. + */ + default void validateSoapMessage (@NonNull final Document aSoapDocument, @NonNull ESoapVersion eSoapVersion, @NonNull final ErrorList aErrorList) + {} + + /** + * Validation of a UserMessage * * @param aUserMsg * The message to be validated. May not be null. @@ -148,7 +163,7 @@ default void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @Non {} /** - * Validation a SignalMessage + * Validation of a SignalMessage * * @param aSignalMsg * The message to be validated. May not be null. diff --git a/phase4-profile-bdew/src/main/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidator.java b/phase4-profile-bdew/src/main/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidator.java index c6fafc364..ff1d6168e 100644 --- a/phase4-profile-bdew/src/main/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidator.java +++ b/phase4-profile-bdew/src/main/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidator.java @@ -17,7 +17,9 @@ package com.helger.phase4.profile.bdew; import java.security.cert.X509Certificate; +import java.util.EnumSet; +import com.helger.xml.XMLHelper; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.BCStyle; @@ -63,6 +65,9 @@ import com.helger.phase4.model.pmode.leg.PModeLegSecurity; import com.helger.phase4.profile.IAS4ProfileValidator; import com.helger.phase4.wss.EWSSVersion; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; /** * Validate certain requirements imposed by the BDEW project. @@ -518,10 +523,58 @@ public void validateInitiatorIdentity (@NonNull final Ebms3UserMessage aUserMsg, } } + @Override + public void validateSoapMessage(@NonNull Document aSoapDocument, @NonNull ESoapVersion eSoapVersion, @NonNull ErrorList aErrorList) + { + ValueEnforcer.notNull (aSoapDocument, "SoapDocument"); + ValueEnforcer.notNull (aErrorList, "SoapVersion"); + ValueEnforcer.notNull (aErrorList, "ErrorList"); + + final Element aEnvelope = aSoapDocument.getDocumentElement (); + if (aEnvelope == null) + { + aErrorList.add (_createError ("SOAP Envelope is missing")); + return; + } + + Element aBodyElement = XMLHelper.getFirstChildElementOfName (aEnvelope, + eSoapVersion.getNamespaceURI (), + eSoapVersion.getBodyElementName ()); + + if (aBodyElement == null) + { + aErrorList.add (_createError ("SOAP Body is missing")); + return; + } + + if (!_isSoapBodyEmpty (aBodyElement)) + aErrorList.add (_createError ("SOAP Body must be empty")); + } + + private static boolean _isSoapBodyEmpty (@NonNull final Element aBodyElement) + { + for (Node aChild = aBodyElement.getFirstChild (); aChild != null; aChild = aChild.getNextSibling ()) + switch (aChild.getNodeType ()) + { + case Node.ELEMENT_NODE: + return false; + case Node.TEXT_NODE, Node.CDATA_SECTION_NODE: + final String sNodeValue = aChild.getNodeValue (); + if (sNodeValue != null && !sNodeValue.trim ().isEmpty ()) + return false; + break; + default: + // Ignore comments and processing instructions + break; + } + return true; + } + @Override public void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @NonNull final ErrorList aErrorList) { ValueEnforcer.notNull (aUserMsg, "UserMsg"); + ValueEnforcer.notNull (aErrorList, "ErrorList"); if (aUserMsg.getMessageInfo () == null) { @@ -637,6 +690,7 @@ public void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @NonN public void validateSignalMessage (@NonNull final Ebms3SignalMessage aSignalMsg, @NonNull final ErrorList aErrorList) { ValueEnforcer.notNull (aSignalMsg, "SignalMsg"); + ValueEnforcer.notNull (aErrorList, "ErrorList"); if (aSignalMsg.getMessageInfo () == null) { @@ -648,4 +702,9 @@ public void validateSignalMessage (@NonNull final Ebms3SignalMessage aSignalMsg, aErrorList.add (_createError ("MessageInfo/MessageId is missing")); } } + + @Override + public @NonNull EnumSet getRequiredSignedParts(boolean bMessageHasAttachments) { + return EnumSet.of(ESignedPart.EBMS_MESSAGING, ESignedPart.ATTACHMENTS); + } } diff --git a/phase4-profile-bdew/src/test/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidatorTest.java b/phase4-profile-bdew/src/test/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidatorTest.java index 6c86eafb6..4c8f5e74f 100644 --- a/phase4-profile-bdew/src/test/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidatorTest.java +++ b/phase4-profile-bdew/src/test/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidatorTest.java @@ -16,6 +16,7 @@ */ package com.helger.phase4.profile.bdew; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; @@ -29,6 +30,7 @@ import java.util.Locale; import java.util.UUID; +import com.helger.xml.serialize.read.DOMReader; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.Before; import org.junit.BeforeClass; @@ -69,6 +71,7 @@ import com.helger.phase4.profile.IAS4ProfileValidator.EAS4ProfileValidationMode; import com.helger.phase4.wss.EWSSVersion; import com.helger.photon.app.mock.PhotonAppWebTestRule; +import org.w3c.dom.Document; /** * All essentials need to be set and need to be not null since they are getting @@ -632,6 +635,35 @@ public void testValidatePModeCorrect () assertTrue (m_aErrorList.isEmpty ()); } + @Test + public void testValidateSoapDocumentHasEmptyBody () + { + final Document aSoapDoc = DOMReader.readXMLDOM (""" + + + + """); + assertNotNull (aSoapDoc); + + VALIDATOR.validateSoapMessage (aSoapDoc, ESoapVersion.SOAP_12, m_aErrorList); + assertTrue (m_aErrorList.isEmpty ()); + } + + @Test + public void testValidateSoapDocumentHasPayloadInBody () + { + final Document aSoapDoc = DOMReader.readXMLDOM (""" + + + + """); + assertNotNull (aSoapDoc); + + VALIDATOR.validateSoapMessage (aSoapDoc, ESoapVersion.SOAP_12, m_aErrorList); + assertFalse (m_aErrorList.isEmpty ()); + assertTrue (m_aErrorList.containsAny (x -> x.getErrorText (LOCALE).contains ("SOAP Body must be empty"))); + } + @Test public void testValidateUserMessageNoMessageInfo () {