Skip to content

Commit 1a53f5d

Browse files
authored
BDEW - SOAP Body must be empty and body signature is ALWAYS optional (#396)
1 parent 02849a0 commit 1a53f5d

4 files changed

Lines changed: 109 additions & 2 deletions

File tree

phase4-lib/src/main/java/com/helger/phase4/incoming/AS4IncomingHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ public static IAS4IncomingMessageState processEbmsMessage (@NonNull @WillNotClos
10721072
{
10731073
final ErrorList aErrorList = new ErrorList ();
10741074
aValidator.validatePMode (aPMode, aErrorList, EAS4ProfileValidationMode.USER_MESSAGE);
1075+
aValidator.validateSoapMessage (aSoapDocument, aIncomingState.getSoapVersion(), aErrorList);
10751076
aValidator.validateUserMessage (aEbmsUserMessage, aErrorList);
10761077
aValidator.validateInitiatorIdentity (aEbmsUserMessage,
10771078
aIncomingState.getSigningCertificate (),

phase4-lib/src/main/java/com/helger/phase4/profile/IAS4ProfileValidator.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.security.cert.X509Certificate;
2020
import java.util.EnumSet;
2121

22+
import com.helger.phase4.model.ESoapVersion;
2223
import org.jspecify.annotations.NonNull;
2324
import org.jspecify.annotations.Nullable;
2425

@@ -29,6 +30,7 @@
2930
import com.helger.phase4.ebms3header.Ebms3UserMessage;
3031
import com.helger.phase4.incoming.IAS4IncomingMessageMetadata;
3132
import com.helger.phase4.model.pmode.IPMode;
33+
import org.w3c.dom.Document;
3234

3335
/**
3436
* Generic AS4 profile validator
@@ -137,7 +139,20 @@ default void validateInitiatorIdentity (@NonNull final Ebms3UserMessage aUserMsg
137139
{}
138140

139141
/**
140-
* Validation a UserMessage
142+
* Validation of a SoapDocument
143+
*
144+
* @param aSoapDocument
145+
* The SOAP document to be validated. May not be <code>null</code>.
146+
* @param eSoapVersion
147+
* The SOAP version of the document to be validated.
148+
* @param aErrorList
149+
* The error list to be filled. May not be <code>null</code>.
150+
*/
151+
default void validateSoapMessage (@NonNull final Document aSoapDocument, @NonNull ESoapVersion eSoapVersion, @NonNull final ErrorList aErrorList)
152+
{}
153+
154+
/**
155+
* Validation of a UserMessage
141156
*
142157
* @param aUserMsg
143158
* The message to be validated. May not be <code>null</code>.
@@ -148,7 +163,7 @@ default void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @Non
148163
{}
149164

150165
/**
151-
* Validation a SignalMessage
166+
* Validation of a SignalMessage
152167
*
153168
* @param aSignalMsg
154169
* The message to be validated. May not be <code>null</code>.

phase4-profile-bdew/src/main/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidator.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package com.helger.phase4.profile.bdew;
1818

1919
import java.security.cert.X509Certificate;
20+
import java.util.EnumSet;
2021

22+
import com.helger.xml.XMLHelper;
2123
import org.bouncycastle.asn1.x500.RDN;
2224
import org.bouncycastle.asn1.x500.X500Name;
2325
import org.bouncycastle.asn1.x500.style.BCStyle;
@@ -63,6 +65,9 @@
6365
import com.helger.phase4.model.pmode.leg.PModeLegSecurity;
6466
import com.helger.phase4.profile.IAS4ProfileValidator;
6567
import com.helger.phase4.wss.EWSSVersion;
68+
import org.w3c.dom.Document;
69+
import org.w3c.dom.Element;
70+
import org.w3c.dom.Node;
6671

6772
/**
6873
* Validate certain requirements imposed by the BDEW project.
@@ -518,10 +523,58 @@ public void validateInitiatorIdentity (@NonNull final Ebms3UserMessage aUserMsg,
518523
}
519524
}
520525

526+
@Override
527+
public void validateSoapMessage(@NonNull Document aSoapDocument, @NonNull ESoapVersion eSoapVersion, @NonNull ErrorList aErrorList)
528+
{
529+
ValueEnforcer.notNull (aSoapDocument, "SoapDocument");
530+
ValueEnforcer.notNull (aErrorList, "SoapVersion");
531+
ValueEnforcer.notNull (aErrorList, "ErrorList");
532+
533+
final Element aEnvelope = aSoapDocument.getDocumentElement ();
534+
if (aEnvelope == null)
535+
{
536+
aErrorList.add (_createError ("SOAP Envelope is missing"));
537+
return;
538+
}
539+
540+
Element aBodyElement = XMLHelper.getFirstChildElementOfName (aEnvelope,
541+
eSoapVersion.getNamespaceURI (),
542+
eSoapVersion.getBodyElementName ());
543+
544+
if (aBodyElement == null)
545+
{
546+
aErrorList.add (_createError ("SOAP Body is missing"));
547+
return;
548+
}
549+
550+
if (!_isSoapBodyEmpty (aBodyElement))
551+
aErrorList.add (_createError ("SOAP Body must be empty"));
552+
}
553+
554+
private static boolean _isSoapBodyEmpty (@NonNull final Element aBodyElement)
555+
{
556+
for (Node aChild = aBodyElement.getFirstChild (); aChild != null; aChild = aChild.getNextSibling ())
557+
switch (aChild.getNodeType ())
558+
{
559+
case Node.ELEMENT_NODE:
560+
return false;
561+
case Node.TEXT_NODE, Node.CDATA_SECTION_NODE:
562+
final String sNodeValue = aChild.getNodeValue ();
563+
if (sNodeValue != null && !sNodeValue.trim ().isEmpty ())
564+
return false;
565+
break;
566+
default:
567+
// Ignore comments and processing instructions
568+
break;
569+
}
570+
return true;
571+
}
572+
521573
@Override
522574
public void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @NonNull final ErrorList aErrorList)
523575
{
524576
ValueEnforcer.notNull (aUserMsg, "UserMsg");
577+
ValueEnforcer.notNull (aErrorList, "ErrorList");
525578

526579
if (aUserMsg.getMessageInfo () == null)
527580
{
@@ -637,6 +690,7 @@ public void validateUserMessage (@NonNull final Ebms3UserMessage aUserMsg, @NonN
637690
public void validateSignalMessage (@NonNull final Ebms3SignalMessage aSignalMsg, @NonNull final ErrorList aErrorList)
638691
{
639692
ValueEnforcer.notNull (aSignalMsg, "SignalMsg");
693+
ValueEnforcer.notNull (aErrorList, "ErrorList");
640694

641695
if (aSignalMsg.getMessageInfo () == null)
642696
{
@@ -648,4 +702,9 @@ public void validateSignalMessage (@NonNull final Ebms3SignalMessage aSignalMsg,
648702
aErrorList.add (_createError ("MessageInfo/MessageId is missing"));
649703
}
650704
}
705+
706+
@Override
707+
public @NonNull EnumSet<ESignedPart> getRequiredSignedParts(boolean bMessageHasAttachments) {
708+
return EnumSet.of(ESignedPart.EBMS_MESSAGING, ESignedPart.ATTACHMENTS);
709+
}
651710
}

phase4-profile-bdew/src/test/java/com/helger/phase4/profile/bdew/BDEWCompatibilityValidatorTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.helger.phase4.profile.bdew;
1818

19+
import static org.junit.Assert.assertFalse;
1920
import static org.junit.Assert.assertNotNull;
2021
import static org.junit.Assert.assertNotSame;
2122
import static org.junit.Assert.assertTrue;
@@ -29,6 +30,7 @@
2930
import java.util.Locale;
3031
import java.util.UUID;
3132

33+
import com.helger.xml.serialize.read.DOMReader;
3234
import org.bouncycastle.jce.provider.BouncyCastleProvider;
3335
import org.junit.Before;
3436
import org.junit.BeforeClass;
@@ -69,6 +71,7 @@
6971
import com.helger.phase4.profile.IAS4ProfileValidator.EAS4ProfileValidationMode;
7072
import com.helger.phase4.wss.EWSSVersion;
7173
import com.helger.photon.app.mock.PhotonAppWebTestRule;
74+
import org.w3c.dom.Document;
7275

7376
/**
7477
* All essentials need to be set and need to be not null since they are getting
@@ -632,6 +635,35 @@ public void testValidatePModeCorrect ()
632635
assertTrue (m_aErrorList.isEmpty ());
633636
}
634637

638+
@Test
639+
public void testValidateSoapDocumentHasEmptyBody ()
640+
{
641+
final Document aSoapDoc = DOMReader.readXMLDOM ("""
642+
<S12:Envelope xmlns:S12='http://www.w3.org/2003/05/soap-envelope'>
643+
<S12:Header/>
644+
<S12:Body> </S12:Body>
645+
</S12:Envelope>""");
646+
assertNotNull (aSoapDoc);
647+
648+
VALIDATOR.validateSoapMessage (aSoapDoc, ESoapVersion.SOAP_12, m_aErrorList);
649+
assertTrue (m_aErrorList.isEmpty ());
650+
}
651+
652+
@Test
653+
public void testValidateSoapDocumentHasPayloadInBody ()
654+
{
655+
final Document aSoapDoc = DOMReader.readXMLDOM ("""
656+
<S12:Envelope xmlns:S12='http://www.w3.org/2003/05/soap-envelope'>
657+
<S12:Header/>
658+
<S12:Body><payload/></S12:Body>
659+
</S12:Envelope>""");
660+
assertNotNull (aSoapDoc);
661+
662+
VALIDATOR.validateSoapMessage (aSoapDoc, ESoapVersion.SOAP_12, m_aErrorList);
663+
assertFalse (m_aErrorList.isEmpty ());
664+
assertTrue (m_aErrorList.containsAny (x -> x.getErrorText (LOCALE).contains ("SOAP Body must be empty")));
665+
}
666+
635667
@Test
636668
public void testValidateUserMessageNoMessageInfo ()
637669
{

0 commit comments

Comments
 (0)