This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new 64459b6299c0 [backport camel-4.18.x] CAMEL-24417: camel-as2 - validate
the asynchronous MDN delivery address and scope the MDN credentials (#25764)
64459b6299c0 is described below
commit 64459b6299c07364eb9a225ee02fa2153da526e2
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Aug 27 06:40:55 2026 +0200
[backport camel-4.18.x] CAMEL-24417: camel-as2 - validate the asynchronous
MDN delivery address and scope the MDN credentials (#25764)
CAMEL-24417: camel-as2 - validate the asynchronous MDN delivery address and
scope the MDN credentials
The delivery address for an asynchronous MDN comes from the
Receipt-Delivery-Option
header of the received AS2 message, so it is chosen by the sender. The
server passed it
straight to a socket and attached the configured MDN credentials to
whatever host it
named, with no scheme check.
A new asyncMdnAllowedHosts option names the hosts an asynchronous MDN may
be delivered
to. Credentials are attached only for a host on the list, and an address
naming a host
outside it is refused. With the option unset the MDN is still delivered, as
before, but
no credentials are attached.
Independently of the option the scheme must now be http. https is refused
rather than
attempted: this class delivers over a plain socket and has no TLS, so an
https address
wrote the request - including the Authorization header - in cleartext to
the TLS port.
IPv6 hosts are unbracketed before the allow-list match.
Public API stays source and binary compatible; the previous constructors
delegate. Real
TLS delivery via SSLContextParameters is tracked as CAMEL-24474.
Closes #25637
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../org/apache/camel/catalog/components/as2.json | 31 ++++---
.../as2/api/AS2AsynchronousMDNManager.java | 84 ++++++++++++++++-
.../component/as2/api/AS2ServerConnection.java | 26 +++++-
...2AsynchronousMDNManagerDeliveryAddressTest.java | 102 +++++++++++++++++++++
...rverManagerEndpointConfigurationConfigurer.java | 7 ++
...ientManagerEndpointConfigurationConfigurer.java | 7 ++
.../component/as2/AS2ConfigurationConfigurer.java | 7 ++
.../camel/component/as2/AS2EndpointConfigurer.java | 7 ++
.../camel/component/as2/AS2EndpointUriFactory.java | 3 +-
...rverManagerEndpointConfigurationConfigurer.java | 7 ++
.../org/apache/camel/component/as2/as2.json | 31 ++++---
.../camel/component/as2/AS2Configuration.java | 18 ++++
.../as2/internal/AS2ConnectionHelper.java | 3 +-
.../as2/AS2AsyncMdnBasicAuthHeaderTest.java | 5 +-
.../as2/AS2AsyncMdnTokenAuthHeaderTest.java | 5 +-
.../as2/AS2ServerBasicAuthHeaderTest.java | 3 +
.../as2/AS2ServerBearerAuthHeaderTest.java | 3 +
.../endpoint/dsl/AS2EndpointBuilderFactory.java | 63 +++++++++++++
18 files changed, 375 insertions(+), 37 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
index 4e0f84179c00..5713cb8a636e 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/as2.json
@@ -67,21 +67,22 @@
"exchangePattern": { "index": 31, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." },
"lazyStartProducer": { "index": 32, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
"accessToken": { "index": 33, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the client
for bearer authentication." },
- "decryptingPrivateKey": { "index": 34, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
- "encryptingAlgorithm": { "index": 35, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
- "encryptingCertificateChain": { "index": 36, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
- "hostnameVerifier": { "index": 37, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
- "mdnAccessToken": { "index": 38, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
- "mdnPassword": { "index": 39, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async MDN." },
- "mdnUserName": { "index": 40, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an async MDN. If options for [...]
- "password": { "index": 41, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
- "signedReceiptMicAlgorithms": { "index": 42, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
- "signingAlgorithm": { "index": 43, "kind": "parameter", "displayName":
"Signing Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [
"SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA",
"MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA",
"SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA",
"RIPEMD256 [...]
- "signingCertificateChain": { "index": 44, "kind": "parameter",
"displayName": "Signing Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to sign EDI
message." },
- "signingPrivateKey": { "index": 45, "kind": "parameter", "displayName":
"Signing Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to sign the EDI message." },
- "sslContext": { "index": 46, "kind": "parameter", "displayName": "Ssl
Context", "group": "security", "label": "security", "required": false, "type":
"object", "javaType": "javax.net.ssl.SSLContext", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set SSL context for connection to remote
server." },
- "userName": { "index": 47, "kind": "parameter", "displayName": "User
Name", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the client for
basic authentication. If options for basic authentication and bearer au [...]
- "validateSigningCertificateChain": { "index": 48, "kind": "parameter",
"displayName": "Validate Signing Certificate Chain", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Certificates to validate the message's
signature ag [...]
+ "asyncMdnAllowedHosts": { "index": 34, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
+ "decryptingPrivateKey": { "index": 35, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
+ "encryptingAlgorithm": { "index": 36, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
+ "encryptingCertificateChain": { "index": 37, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
+ "hostnameVerifier": { "index": 38, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
+ "mdnAccessToken": { "index": 39, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
+ "mdnPassword": { "index": 40, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async MDN." },
+ "mdnUserName": { "index": 41, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an async MDN. If options for [...]
+ "password": { "index": 42, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
+ "signedReceiptMicAlgorithms": { "index": 43, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
+ "signingAlgorithm": { "index": 44, "kind": "parameter", "displayName":
"Signing Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [
"SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA",
"MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA",
"SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA",
"RIPEMD256 [...]
+ "signingCertificateChain": { "index": 45, "kind": "parameter",
"displayName": "Signing Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to sign EDI
message." },
+ "signingPrivateKey": { "index": 46, "kind": "parameter", "displayName":
"Signing Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to sign the EDI message." },
+ "sslContext": { "index": 47, "kind": "parameter", "displayName": "Ssl
Context", "group": "security", "label": "security", "required": false, "type":
"object", "javaType": "javax.net.ssl.SSLContext", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set SSL context for connection to remote
server." },
+ "userName": { "index": 48, "kind": "parameter", "displayName": "User
Name", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the client for
basic authentication. If options for basic authentication and bearer au [...]
+ "validateSigningCertificateChain": { "index": 49, "kind": "parameter",
"displayName": "Validate Signing Certificate Chain", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Certificates to validate the message's
signature ag [...]
},
"apis": {
"client": { "consumerOnly": false, "producerOnly": true, "description":
"Sends EDI Messages over HTTP", "methods": { "send": { "description": "Send
ediMessage to trading partner", "signatures": [
"org.apache.hc.core5.http.protocol.HttpCoreContext send(Object ediMessage,
String requestUri, String subject, String from, String as2From, String as2To,
org.apache.camel.component.as2.api.AS2MessageStructure as2MessageStructure,
String ediMessageContentType, String ediMessageCharset, String [...]
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManager.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManager.java
index 3a922227d4ac..ed8b3b2e709f 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManager.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManager.java
@@ -21,6 +21,7 @@ import java.net.Socket;
import java.net.URI;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.util.Locale;
import org.apache.camel.component.as2.api.entity.MultipartMimeEntity;
import org.apache.camel.component.as2.api.protocol.RequestAsynchronousMDN;
@@ -46,9 +47,13 @@ import org.apache.hc.core5.http.protocol.RequestContent;
import org.apache.hc.core5.http.protocol.RequestDate;
import org.apache.hc.core5.http.protocol.RequestTargetHost;
import org.apache.hc.core5.http.protocol.RequestUserAgent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class AS2AsynchronousMDNManager {
+ private static final Logger LOG =
LoggerFactory.getLogger(AS2AsynchronousMDNManager.class);
+
//
// AS2 HTTP Context Attribute Keys
//
@@ -92,7 +97,15 @@ public class AS2AsynchronousMDNManager {
private String userName;
private String password;
private String accessToken;
+ private String allowedHosts;
+ /**
+ * @deprecated use
+ * {@link #AS2AsynchronousMDNManager(String, String, String,
Certificate[], PrivateKey, String, String, String, String)}
+ * which also takes the allowed delivery hosts. This
constructor delivers the MDN without attaching the
+ * configured credentials, because no allow-list is supplied.
+ */
+ @Deprecated
public AS2AsynchronousMDNManager(String as2Version,
String userAgent,
String senderFQDN,
@@ -101,6 +114,20 @@ public class AS2AsynchronousMDNManager {
String userName,
String password,
String accessToken) {
+ this(as2Version, userAgent, senderFQDN, signingCertificateChain,
signingPrivateKey, userName, password,
+ accessToken, null);
+ }
+
+ public AS2AsynchronousMDNManager(String as2Version,
+ String userAgent,
+ String senderFQDN,
+ Certificate[] signingCertificateChain,
+ PrivateKey signingPrivateKey,
+ String userName,
+ String password,
+ String accessToken,
+ String allowedHosts) {
+ this.allowedHosts = allowedHosts;
this.signingCertificateChain = signingCertificateChain;
this.signingPrivateKey = signingPrivateKey;
this.userName = userName;
@@ -124,7 +151,29 @@ public class AS2AsynchronousMDNManager {
ObjectHelper.notNull(contentType, "contentType");
ObjectHelper.notNull(recipientDeliveryAddress,
"recipientDeliveryAddress");
+ // The delivery address is chosen by the sender of the AS2 message
(the Receipt-Delivery-Option
+ // header), so it is untrusted input that selects an outbound
destination.
URI uri = URI.create(recipientDeliveryAddress);
+ String scheme = uri.getScheme() == null ? null :
uri.getScheme().toLowerCase(Locale.US);
+ // Only http. This class delivers over a plain Socket and has no TLS
of any kind, so accepting https
+ // would mean writing the request - including the Authorization header
- in cleartext to the TLS port.
+ // https delivery has never worked here for that reason, so refusing
it removes nothing that functioned.
+ if (!"http".equals(scheme)) {
+ throw new HttpException(
+ "Refusing to deliver the asynchronous MDN: the delivery
address must use http."
+ + " TLS delivery of asynchronous MDNs is
not supported");
+ }
+ String host = normalizeHost(uri.getHost());
+ if (host == null) {
+ throw new HttpException("Refusing to deliver the asynchronous MDN:
the delivery address has no host");
+ }
+ int port = uri.getPort() != -1 ? uri.getPort() : 80;
+
+ boolean hostIsAllowed = isAllowedHost(host);
+ if (allowedHosts != null && !allowedHosts.isBlank() && !hostIsAllowed)
{
+ throw new HttpException(
+ "Refusing to deliver the asynchronous MDN: the delivery
address host is not in asyncMdnAllowedHosts");
+ }
int buffSize = 8 * 1024;
@@ -132,7 +181,7 @@ public class AS2AsynchronousMDNManager {
HttpConnectionFactory<ManagedHttpClientConnection> connFactory
=
ManagedHttpClientConnectionFactory.builder().http1Config(h1Config).build();
- try (HttpClientConnection httpConnection =
connFactory.createConnection(new Socket(uri.getHost(), uri.getPort()))) {
+ try (HttpClientConnection httpConnection =
connFactory.createConnection(new Socket(host, port))) {
// Add Context attributes
HttpCoreContext httpContext = HttpCoreContext.create();
@@ -140,7 +189,15 @@ public class AS2AsynchronousMDNManager {
ClassicHttpRequest request = new BasicClassicHttpRequest("POST",
uri);
request.setHeader(AS2Header.CONTENT_TYPE, contentType);
- AS2HeaderUtils.addAuthorizationHeader(request, userName, password,
accessToken);
+ // Credentials are only attached to a host the operator has
vouched for. Without an allow-list the
+ // destination is entirely sender-chosen, so the MDN is still
delivered but without them.
+ if (hostIsAllowed) {
+ AS2HeaderUtils.addAuthorizationHeader(request, userName,
password, accessToken);
+ } else if (userName != null || accessToken != null) {
+ LOG.warn("Asynchronous MDN credentials not sent to
sender-supplied host {}:"
+ + " set asyncMdnAllowedHosts to authorise it",
+ host);
+ }
httpContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
multipartMimeEntity.setMainBody(true);
EntityUtils.setMessageEntity(request, multipartMimeEntity);
@@ -160,6 +217,29 @@ public class AS2AsynchronousMDNManager {
}
}
+ /**
+ * {@link URI#getHost()} returns an IPv6 literal in its bracketed form
({@code [::1]}), which would never match an
+ * allow-list entry written the way an operator writes it. Compare the
address itself.
+ */
+ private static String normalizeHost(String host) {
+ if (host != null && host.length() > 1 && host.charAt(0) == '[' &&
host.charAt(host.length() - 1) == ']') {
+ return host.substring(1, host.length() - 1);
+ }
+ return host;
+ }
+
+ private boolean isAllowedHost(String host) {
+ if (allowedHosts == null || allowedHosts.isBlank()) {
+ return false;
+ }
+ for (String allowed : allowedHosts.split(",")) {
+ if (allowed.trim().equalsIgnoreCase(host)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private HttpResponse send(HttpClientConnection httpConnection,
ClassicHttpRequest request, HttpCoreContext httpContext)
throws HttpException, IOException {
diff --git
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
index cf4b5c79ebc5..895cf21af273 100644
---
a/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
+++
b/components/camel-as2/camel-as2-api/src/main/java/org/apache/camel/component/as2/api/AS2ServerConnection.java
@@ -90,6 +90,7 @@ public class AS2ServerConnection {
private final String userName;
private final String password;
private final String accessToken;
+ private final String asyncMdnAllowedHosts;
/**
* Stores the configuration for each consumer endpoint path (e.g.,
"/consumerA"). Uses LinkedHashMap to preserve
@@ -496,7 +497,8 @@ public class AS2ServerConnection {
config.getSigningPrivateKey(),
AS2ServerConnection.this.userName,
AS2ServerConnection.this.password,
- AS2ServerConnection.this.accessToken);
+ AS2ServerConnection.this.accessToken,
+ AS2ServerConnection.this.asyncMdnAllowedHosts);
HttpRequest request =
coreContext.getAttribute(HttpCoreContext.HTTP_REQUEST, HttpRequest.class);
AS2SignedDataGenerator gen =
ResponseMDN.createSigningGenerator(
@@ -556,6 +558,28 @@ public class AS2ServerConnection {
String password,
String accessToken)
throws IOException {
+ this(as2Version, originServer, serverFqdn, serverPortNumber,
signingAlgorithm, signingCertificateChain,
+ signingPrivateKey, decryptingPrivateKey, mdnMessageTemplate,
validateSigningCertificateChain, sslContext,
+ userName, password, accessToken, null);
+ }
+
+ public AS2ServerConnection(String as2Version,
+ String originServer,
+ String serverFqdn,
+ Integer serverPortNumber,
+ AS2SignatureAlgorithm signingAlgorithm,
+ Certificate[] signingCertificateChain,
+ PrivateKey signingPrivateKey,
+ PrivateKey decryptingPrivateKey,
+ String mdnMessageTemplate,
+ Certificate[] validateSigningCertificateChain,
+ SSLContext sslContext,
+ String userName,
+ String password,
+ String accessToken,
+ String asyncMdnAllowedHosts)
+ throws IOException
{
+ this.asyncMdnAllowedHosts = asyncMdnAllowedHosts;
this.as2Version = ObjectHelper.notNull(as2Version, "as2Version");
this.originServer = ObjectHelper.notNull(originServer, "userAgent");
this.serverFqdn = ObjectHelper.notNull(serverFqdn, "serverFqdn");
diff --git
a/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManagerDeliveryAddressTest.java
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManagerDeliveryAddressTest.java
new file mode 100644
index 000000000000..09ea7b6cca42
--- /dev/null
+++
b/components/camel-as2/camel-as2-api/src/test/java/org/apache/camel/component/as2/api/AS2AsynchronousMDNManagerDeliveryAddressTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.as2.api;
+
+import org.apache.camel.component.as2.api.entity.MultipartMimeEntity;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The delivery address for an asynchronous MDN comes from the {@code
Receipt-Delivery-Option} header of the received
+ * AS2 message, so it is chosen by the sender. It selects an outbound
destination and, before this was constrained, the
+ * configured MDN credentials went with it.
+ */
+class AS2AsynchronousMDNManagerDeliveryAddressTest {
+
+ private static final String ALLOWED = "partner.example";
+
+ /**
+ * https in particular: this class delivers over a plain {@link
java.net.Socket} and has no TLS, so accepting an
+ * https address would write the request - including the Authorization
header - in cleartext to the TLS port. Before
+ * the delivery address was validated at all, an https address without an
explicit port failed closed on
+ * {@code new Socket(host, -1)}; resolving it to 443 instead would have
been a regression.
+ */
+ @Test
+ void aSchemeOtherThanHttpIsRefused() {
+ for (String address : new String[] {
+ "https://partner.example/receipts",
"https://partner.example:443/receipts",
+ "file:///etc/passwd", "ftp://partner.example/x",
"gopher://partner.example:70/x", "//partner.example/x" }) {
+ HttpException e = assertThrows(HttpException.class, () ->
deliver(address, ALLOWED),
+ "expected " + address + " to be refused");
+ assertTrue(e.getMessage().contains("must use http") ||
e.getMessage().contains("no host"),
+ "unexpected message for " + address + ": " +
e.getMessage());
+ }
+ }
+
+ @Test
+ void aHostOutsideTheAllowListIsRefused() {
+ HttpException e = assertThrows(HttpException.class,
+ () -> deliver("http://attacker.example/receipts", ALLOWED));
+ assertTrue(e.getMessage().contains("asyncMdnAllowedHosts"),
"unexpected message: " + e.getMessage());
+ }
+
+ /**
+ * With no allow-list the MDN is still delivered, so this gets past the
address checks and fails on the connection
+ * instead - which is what tells us the address itself was accepted.
+ */
+ @Test
+ void withNoAllowListTheAddressIsStillAccepted() {
+ Exception e = assertThrows(Exception.class,
+ () -> deliver("http://localhost:1/receipts", null));
+ assertTrue(!(e instanceof HttpException) ||
!e.getMessage().contains("asyncMdnAllowedHosts"),
+ "the address must not be refused when no allow-list is
configured: " + e.getMessage());
+ }
+
+ @Test
+ void anIpv6AllowListEntryIsMatchedWithoutItsBrackets() {
+ // URI.getHost() yields "[::1]", but an operator writes "::1" in the
allow-list
+ Exception e = assertThrows(Exception.class, () ->
deliver("http://[::1]:1/receipts", "::1"));
+ assertTrue(!(e instanceof HttpException) ||
!e.getMessage().contains("asyncMdnAllowedHosts"),
+ "an IPv6 literal should match its unbracketed allow-list
entry: " + e.getMessage());
+ }
+
+ private static void deliver(String deliveryAddress, String allowedHosts)
throws Exception {
+ AS2AsynchronousMDNManager manager = new AS2AsynchronousMDNManager(
+ "1.1", "Camel", "sender.example.com", null, null, "user",
"password", null, allowedHosts);
+ manager.send(new TestEntity(), AS2MimeType.MULTIPART_REPORT,
deliveryAddress);
+ }
+
+ /**
+ * The manager null-checks its entity before it looks at the delivery
address, so the address checks need a real
+ * one. Its content is irrelevant here - none of these cases reach the
point of writing it.
+ */
+ private static final class TestEntity extends MultipartMimeEntity {
+
+ private TestEntity() {
+ super(ContentType.create(AS2MimeType.MULTIPART_REPORT), "7bit");
+ }
+
+ @Override
+ public void close() {
+ // nothing to release
+ }
+ }
+}
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2AsyncMDNServerManagerEndpointConfigurationConfigurer.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2AsyncMDNServerManagerEndpointConfigurationConfigurer.java
index 214852589959..b5d43287bf4d 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2AsyncMDNServerManagerEndpointConfigurationConfigurer.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2AsyncMDNServerManagerEndpointConfigurationConfigurer.java
@@ -28,6 +28,7 @@ public class
AS2AsyncMDNServerManagerEndpointConfigurationConfigurer extends org
map.put("As2MessageStructure",
org.apache.camel.component.as2.api.AS2MessageStructure.class);
map.put("As2To", java.lang.String.class);
map.put("As2Version", java.lang.String.class);
+ map.put("AsyncMdnAllowedHosts", java.lang.String.class);
map.put("AsyncMdnPortNumber", java.lang.Integer.class);
map.put("AttachedFileName", java.lang.String.class);
map.put("ClientFqdn", java.lang.String.class);
@@ -87,6 +88,8 @@ public class
AS2AsyncMDNServerManagerEndpointConfigurationConfigurer extends org
case "as2To": target.setAs2To(property(camelContext,
java.lang.String.class, value)); return true;
case "as2version":
case "as2Version": target.setAs2Version(property(camelContext,
java.lang.String.class, value)); return true;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts":
target.setAsyncMdnAllowedHosts(property(camelContext, java.lang.String.class,
value)); return true;
case "asyncmdnportnumber":
case "asyncMdnPortNumber":
target.setAsyncMdnPortNumber(property(camelContext, java.lang.Integer.class,
value)); return true;
case "attachedfilename":
@@ -187,6 +190,8 @@ public class
AS2AsyncMDNServerManagerEndpointConfigurationConfigurer extends org
case "as2To": return java.lang.String.class;
case "as2version":
case "as2Version": return java.lang.String.class;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return java.lang.String.class;
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return java.lang.Integer.class;
case "attachedfilename":
@@ -283,6 +288,8 @@ public class
AS2AsyncMDNServerManagerEndpointConfigurationConfigurer extends org
case "as2To": return target.getAs2To();
case "as2version":
case "as2Version": return target.getAs2Version();
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return target.getAsyncMdnAllowedHosts();
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return target.getAsyncMdnPortNumber();
case "attachedfilename":
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfigurationConfigurer.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfigurationConfigurer.java
index f24e86349b8d..6712d20d0868 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfigurationConfigurer.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ClientManagerEndpointConfigurationConfigurer.java
@@ -28,6 +28,7 @@ public class AS2ClientManagerEndpointConfigurationConfigurer
extends org.apache.
map.put("As2MessageStructure",
org.apache.camel.component.as2.api.AS2MessageStructure.class);
map.put("As2To", java.lang.String.class);
map.put("As2Version", java.lang.String.class);
+ map.put("AsyncMdnAllowedHosts", java.lang.String.class);
map.put("AsyncMdnPortNumber", java.lang.Integer.class);
map.put("AttachedFileName", java.lang.String.class);
map.put("ClientFqdn", java.lang.String.class);
@@ -88,6 +89,8 @@ public class AS2ClientManagerEndpointConfigurationConfigurer
extends org.apache.
case "as2To": target.setAs2To(property(camelContext,
java.lang.String.class, value)); return true;
case "as2version":
case "as2Version": target.setAs2Version(property(camelContext,
java.lang.String.class, value)); return true;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts":
target.setAsyncMdnAllowedHosts(property(camelContext, java.lang.String.class,
value)); return true;
case "asyncmdnportnumber":
case "asyncMdnPortNumber":
target.setAsyncMdnPortNumber(property(camelContext, java.lang.Integer.class,
value)); return true;
case "attachedfilename":
@@ -190,6 +193,8 @@ public class
AS2ClientManagerEndpointConfigurationConfigurer extends org.apache.
case "as2To": return java.lang.String.class;
case "as2version":
case "as2Version": return java.lang.String.class;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return java.lang.String.class;
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return java.lang.Integer.class;
case "attachedfilename":
@@ -288,6 +293,8 @@ public class
AS2ClientManagerEndpointConfigurationConfigurer extends org.apache.
case "as2To": return target.getAs2To();
case "as2version":
case "as2Version": return target.getAs2Version();
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return target.getAsyncMdnAllowedHosts();
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return target.getAsyncMdnPortNumber();
case "attachedfilename":
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ConfigurationConfigurer.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ConfigurationConfigurer.java
index ae58d63e5403..44315e8155e5 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ConfigurationConfigurer.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ConfigurationConfigurer.java
@@ -28,6 +28,7 @@ public class AS2ConfigurationConfigurer extends
org.apache.camel.support.compone
map.put("As2MessageStructure",
org.apache.camel.component.as2.api.AS2MessageStructure.class);
map.put("As2To", java.lang.String.class);
map.put("As2Version", java.lang.String.class);
+ map.put("AsyncMdnAllowedHosts", java.lang.String.class);
map.put("AsyncMdnPortNumber", java.lang.Integer.class);
map.put("AttachedFileName", java.lang.String.class);
map.put("ClientFqdn", java.lang.String.class);
@@ -86,6 +87,8 @@ public class AS2ConfigurationConfigurer extends
org.apache.camel.support.compone
case "as2To": target.setAs2To(property(camelContext,
java.lang.String.class, value)); return true;
case "as2version":
case "as2Version": target.setAs2Version(property(camelContext,
java.lang.String.class, value)); return true;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts":
target.setAsyncMdnAllowedHosts(property(camelContext, java.lang.String.class,
value)); return true;
case "asyncmdnportnumber":
case "asyncMdnPortNumber":
target.setAsyncMdnPortNumber(property(camelContext, java.lang.Integer.class,
value)); return true;
case "attachedfilename":
@@ -184,6 +187,8 @@ public class AS2ConfigurationConfigurer extends
org.apache.camel.support.compone
case "as2To": return java.lang.String.class;
case "as2version":
case "as2Version": return java.lang.String.class;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return java.lang.String.class;
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return java.lang.Integer.class;
case "attachedfilename":
@@ -278,6 +283,8 @@ public class AS2ConfigurationConfigurer extends
org.apache.camel.support.compone
case "as2To": return target.getAs2To();
case "as2version":
case "as2Version": return target.getAs2Version();
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return target.getAsyncMdnAllowedHosts();
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return target.getAsyncMdnPortNumber();
case "attachedfilename":
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointConfigurer.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointConfigurer.java
index 4de2c44bd707..0eab1fb6e4b0 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointConfigurer.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointConfigurer.java
@@ -56,6 +56,7 @@ public class AS2EndpointConfigurer extends
PropertyConfigurerSupport implements
map.put("ExchangePattern", org.apache.camel.ExchangePattern.class);
map.put("LazyStartProducer", boolean.class);
map.put("AccessToken", java.lang.String.class);
+ map.put("AsyncMdnAllowedHosts", java.lang.String.class);
map.put("DecryptingPrivateKey", java.security.PrivateKey.class);
map.put("EncryptingAlgorithm",
org.apache.camel.component.as2.api.AS2EncryptionAlgorithm.class);
map.put("EncryptingCertificateChain",
java.security.cert.Certificate[].class);
@@ -88,6 +89,8 @@ public class AS2EndpointConfigurer extends
PropertyConfigurerSupport implements
case "as2To":
target.getConfiguration().setAs2To(property(camelContext,
java.lang.String.class, value)); return true;
case "as2version":
case "as2Version":
target.getConfiguration().setAs2Version(property(camelContext,
java.lang.String.class, value)); return true;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts":
target.getConfiguration().setAsyncMdnAllowedHosts(property(camelContext,
java.lang.String.class, value)); return true;
case "asyncmdnportnumber":
case "asyncMdnPortNumber":
target.getConfiguration().setAsyncMdnPortNumber(property(camelContext,
java.lang.Integer.class, value)); return true;
case "attachedfilename":
@@ -190,6 +193,8 @@ public class AS2EndpointConfigurer extends
PropertyConfigurerSupport implements
case "as2To": return java.lang.String.class;
case "as2version":
case "as2Version": return java.lang.String.class;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return java.lang.String.class;
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return java.lang.Integer.class;
case "attachedfilename":
@@ -288,6 +293,8 @@ public class AS2EndpointConfigurer extends
PropertyConfigurerSupport implements
case "as2To": return target.getConfiguration().getAs2To();
case "as2version":
case "as2Version": return target.getConfiguration().getAs2Version();
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return
target.getConfiguration().getAsyncMdnAllowedHosts();
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return
target.getConfiguration().getAsyncMdnPortNumber();
case "attachedfilename":
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointUriFactory.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointUriFactory.java
index 96060d107c99..0d45c9920fff 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointUriFactory.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2EndpointUriFactory.java
@@ -23,13 +23,14 @@ public class AS2EndpointUriFactory extends
org.apache.camel.support.component.En
private static final Set<String> SECRET_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(52);
+ Set<String> props = new HashSet<>(53);
props.add("accessToken");
props.add("apiName");
props.add("as2From");
props.add("as2MessageStructure");
props.add("as2To");
props.add("as2Version");
+ props.add("asyncMdnAllowedHosts");
props.add("asyncMdnPortNumber");
props.add("attachedFileName");
props.add("clientFqdn");
diff --git
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ServerManagerEndpointConfigurationConfigurer.java
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ServerManagerEndpointConfigurationConfigurer.java
index dafc031b2986..da0d11a61f11 100644
---
a/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ServerManagerEndpointConfigurationConfigurer.java
+++
b/components/camel-as2/camel-as2-component/src/generated/java/org/apache/camel/component/as2/AS2ServerManagerEndpointConfigurationConfigurer.java
@@ -28,6 +28,7 @@ public class AS2ServerManagerEndpointConfigurationConfigurer
extends org.apache.
map.put("As2MessageStructure",
org.apache.camel.component.as2.api.AS2MessageStructure.class);
map.put("As2To", java.lang.String.class);
map.put("As2Version", java.lang.String.class);
+ map.put("AsyncMdnAllowedHosts", java.lang.String.class);
map.put("AsyncMdnPortNumber", java.lang.Integer.class);
map.put("AttachedFileName", java.lang.String.class);
map.put("ClientFqdn", java.lang.String.class);
@@ -87,6 +88,8 @@ public class AS2ServerManagerEndpointConfigurationConfigurer
extends org.apache.
case "as2To": target.setAs2To(property(camelContext,
java.lang.String.class, value)); return true;
case "as2version":
case "as2Version": target.setAs2Version(property(camelContext,
java.lang.String.class, value)); return true;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts":
target.setAsyncMdnAllowedHosts(property(camelContext, java.lang.String.class,
value)); return true;
case "asyncmdnportnumber":
case "asyncMdnPortNumber":
target.setAsyncMdnPortNumber(property(camelContext, java.lang.Integer.class,
value)); return true;
case "attachedfilename":
@@ -187,6 +190,8 @@ public class
AS2ServerManagerEndpointConfigurationConfigurer extends org.apache.
case "as2To": return java.lang.String.class;
case "as2version":
case "as2Version": return java.lang.String.class;
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return java.lang.String.class;
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return java.lang.Integer.class;
case "attachedfilename":
@@ -283,6 +288,8 @@ public class
AS2ServerManagerEndpointConfigurationConfigurer extends org.apache.
case "as2To": return target.getAs2To();
case "as2version":
case "as2Version": return target.getAs2Version();
+ case "asyncmdnallowedhosts":
+ case "asyncMdnAllowedHosts": return target.getAsyncMdnAllowedHosts();
case "asyncmdnportnumber":
case "asyncMdnPortNumber": return target.getAsyncMdnPortNumber();
case "attachedfilename":
diff --git
a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
index 4e0f84179c00..5713cb8a636e 100644
---
a/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
+++
b/components/camel-as2/camel-as2-component/src/generated/resources/META-INF/org/apache/camel/component/as2/as2.json
@@ -67,21 +67,22 @@
"exchangePattern": { "index": 31, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." },
"lazyStartProducer": { "index": 32, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
"accessToken": { "index": 33, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the client
for bearer authentication." },
- "decryptingPrivateKey": { "index": 34, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
- "encryptingAlgorithm": { "index": 35, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
- "encryptingCertificateChain": { "index": 36, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
- "hostnameVerifier": { "index": 37, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
- "mdnAccessToken": { "index": 38, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
- "mdnPassword": { "index": 39, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async MDN." },
- "mdnUserName": { "index": 40, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an async MDN. If options for [...]
- "password": { "index": 41, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
- "signedReceiptMicAlgorithms": { "index": 42, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
- "signingAlgorithm": { "index": 43, "kind": "parameter", "displayName":
"Signing Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [
"SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA",
"MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA",
"SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA",
"RIPEMD256 [...]
- "signingCertificateChain": { "index": 44, "kind": "parameter",
"displayName": "Signing Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to sign EDI
message." },
- "signingPrivateKey": { "index": 45, "kind": "parameter", "displayName":
"Signing Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to sign the EDI message." },
- "sslContext": { "index": 46, "kind": "parameter", "displayName": "Ssl
Context", "group": "security", "label": "security", "required": false, "type":
"object", "javaType": "javax.net.ssl.SSLContext", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set SSL context for connection to remote
server." },
- "userName": { "index": 47, "kind": "parameter", "displayName": "User
Name", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the client for
basic authentication. If options for basic authentication and bearer au [...]
- "validateSigningCertificateChain": { "index": 48, "kind": "parameter",
"displayName": "Validate Signing Certificate Chain", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Certificates to validate the message's
signature ag [...]
+ "asyncMdnAllowedHosts": { "index": 34, "kind": "parameter", "displayName":
"Async Mdn Allowed Hosts", "group": "security", "label": "security",
"required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Comma separated list of host names an
asynchronous MDN may be delivered to. The delivery [...]
+ "decryptingPrivateKey": { "index": 35, "kind": "parameter", "displayName":
"Decrypting Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to encrypt the EDI message." },
+ "encryptingAlgorithm": { "index": 36, "kind": "parameter", "displayName":
"Encrypting Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2EncryptionAlgorithm", "enum": [
"AES128_CBC", "AES192_CBC", "AES256_CBC", "AES128_CCM", "AES192_CCM",
"AES256_CCM", "AES128_GCM", "AES192_GCM", "AES256_GCM", "CAMELLIA128_CBC",
"CAMELLIA192_CBC", "CAMELLIA256_CBC", "CAST5_CBC", "DES_CBC", "DES_EDE3_CBC",
"GOST28 [...]
+ "encryptingCertificateChain": { "index": 37, "kind": "parameter",
"displayName": "Encrypting Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to encrypt EDI
message." },
+ "hostnameVerifier": { "index": 38, "kind": "parameter", "displayName":
"Hostname Verifier", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "javax.net.ssl.HostnameVerifier",
"deprecated": false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set hostname verifier for SSL session." },
+ "mdnAccessToken": { "index": 39, "kind": "parameter", "displayName": "Mdn
Access Token", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The access token that is used by the server
when it sends an async MDN." },
+ "mdnPassword": { "index": 40, "kind": "parameter", "displayName": "Mdn
Password", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the server for
basic authentication when it sends an async MDN." },
+ "mdnUserName": { "index": 41, "kind": "parameter", "displayName": "Mdn
User Name", "group": "security", "label": "security", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the server for
basic authentication when it sends an async MDN. If options for [...]
+ "password": { "index": 42, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The password that is used by the client for
basic authentication." },
+ "signedReceiptMicAlgorithms": { "index": 43, "kind": "parameter",
"displayName": "Signed Receipt Mic Algorithms", "group": "security", "label":
"security", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"configurationClass": "org.apache.camel.component.as2.AS2Configuration",
"configurationField": "configuration", "description": "The list of algorithms,
in order of preference, requested to generate a messa [...]
+ "signingAlgorithm": { "index": 44, "kind": "parameter", "displayName":
"Signing Algorithm", "group": "security", "label": "security", "required":
false, "type": "enum", "javaType":
"org.apache.camel.component.as2.api.AS2SignatureAlgorithm", "enum": [
"SHA3_224WITHRSA", "SHA3_256WITHRSA", "SHA3_384withRSA", "SHA3_512WITHRSA",
"MD5WITHRSA", "SHA1WITHRSA", "MD2WITHRSA", "SHA224WITHRSA", "SHA256WITHRSA",
"SHA384WITHRSA", "SHA512WITHRSA", "RIPEMD128WITHRSA", "RIPEMD160WITHRSA",
"RIPEMD256 [...]
+ "signingCertificateChain": { "index": 45, "kind": "parameter",
"displayName": "Signing Certificate Chain", "group": "security", "label":
"security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The chain of certificates used to sign EDI
message." },
+ "signingPrivateKey": { "index": 46, "kind": "parameter", "displayName":
"Signing Private Key", "group": "security", "label": "security", "required":
false, "type": "object", "javaType": "java.security.PrivateKey", "deprecated":
false, "autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The key used to sign the EDI message." },
+ "sslContext": { "index": 47, "kind": "parameter", "displayName": "Ssl
Context", "group": "security", "label": "security", "required": false, "type":
"object", "javaType": "javax.net.ssl.SSLContext", "deprecated": false,
"autowired": false, "secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Set SSL context for connection to remote
server." },
+ "userName": { "index": 48, "kind": "parameter", "displayName": "User
Name", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "The user-name that is used by the client for
basic authentication. If options for basic authentication and bearer au [...]
+ "validateSigningCertificateChain": { "index": 49, "kind": "parameter",
"displayName": "Validate Signing Certificate Chain", "group": "security",
"label": "security", "required": false, "type": "object", "javaType":
"java.security.cert.Certificate[]", "deprecated": false, "autowired": false,
"secret": false, "configurationClass":
"org.apache.camel.component.as2.AS2Configuration", "configurationField":
"configuration", "description": "Certificates to validate the message's
signature ag [...]
},
"apis": {
"client": { "consumerOnly": false, "producerOnly": true, "description":
"Sends EDI Messages over HTTP", "methods": { "send": { "description": "Send
ediMessage to trading partner", "signatures": [
"org.apache.hc.core5.http.protocol.HttpCoreContext send(Object ediMessage,
String requestUri, String subject, String from, String as2From, String as2To,
org.apache.camel.component.as2.api.AS2MessageStructure as2MessageStructure,
String ediMessageContentType, String ediMessageCharset, String [...]
diff --git
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
index 1baa79db7eda..e03d3d8a38f2 100644
---
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
+++
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/AS2Configuration.java
@@ -116,6 +116,8 @@ public class AS2Configuration {
private Certificate[] validateSigningCertificateChain;
@UriParam(label = "security")
private SSLContext sslContext;
+ @UriParam(label = "security")
+ private String asyncMdnAllowedHosts;
// If you use localhost-based AS2 server, you don't need to specify a
hostnameVerifier
@UriParam(label = "security")
private HostnameVerifier hostnameVerifier;
@@ -653,4 +655,20 @@ public class AS2Configuration {
public void setMdnAccessToken(String accessToken) {
this.mdnAccessToken = accessToken;
}
+
+ public String getAsyncMdnAllowedHosts() {
+ return asyncMdnAllowedHosts;
+ }
+
+ /**
+ * Comma separated list of host names an asynchronous MDN may be delivered
to. The delivery address is chosen by the
+ * sender of the AS2 message, so this is the list of hosts the operator
vouches for.
+ *
+ * When set, an asynchronous MDN whose delivery address names a host
outside the list is refused, and the configured
+ * mdnUserName/mdnPassword/mdnAccessToken are attached only to a host on
the list. When not set, the MDN is still
+ * delivered to the sender-supplied address but no credentials are
attached.
+ */
+ public void setAsyncMdnAllowedHosts(String asyncMdnAllowedHosts) {
+ this.asyncMdnAllowedHosts = asyncMdnAllowedHosts;
+ }
}
diff --git
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
index c8d405f9982e..15474412e8df 100644
---
a/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
+++
b/components/camel-as2/camel-as2-component/src/main/java/org/apache/camel/component/as2/internal/AS2ConnectionHelper.java
@@ -110,7 +110,8 @@ public final class AS2ConnectionHelper {
configuration.getValidateSigningCertificateChain(),
configuration.getSslContext(),
configuration.getMdnUserName(),
configuration.getMdnPassword(),
- configuration.getMdnAccessToken());
+ configuration.getMdnAccessToken(),
+ configuration.getAsyncMdnAllowedHosts());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnBasicAuthHeaderTest.java
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnBasicAuthHeaderTest.java
index 28311799b012..52cf5658acf0 100644
---
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnBasicAuthHeaderTest.java
+++
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnBasicAuthHeaderTest.java
@@ -195,7 +195,10 @@ public class AS2AsyncMdnBasicAuthHeaderTest extends
AbstractAS2ITSupport {
null, null, null,
"TBD", null, null,
// server authorization config
- MDN_USER_NAME, MDN_PASSWORD, MDN_ACCESS_TOKEN);
+ MDN_USER_NAME, MDN_PASSWORD, MDN_ACCESS_TOKEN,
+ // the MDN delivery address comes from the
receiptDeliveryOption header, so the host it names
+ // has to be authorised before the credentials above are
attached to it
+ "localhost");
serverConnection.listen("/", new
AS2AsyncMDNServerManagerIT.RequestHandler());
}
}
diff --git
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnTokenAuthHeaderTest.java
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnTokenAuthHeaderTest.java
index 152aef0bd191..62685a588c05 100644
---
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnTokenAuthHeaderTest.java
+++
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2AsyncMdnTokenAuthHeaderTest.java
@@ -147,7 +147,10 @@ public class AS2AsyncMdnTokenAuthHeaderTest extends
AbstractAS2ITSupport {
null, null, null,
"TBD", null, null,
// server authorization config
- null, null, MDN_ACCESS_TOKEN);
+ null, null, MDN_ACCESS_TOKEN,
+ // the MDN delivery address comes from the
receiptDeliveryOption header, so the host it names
+ // has to be authorised before the credentials above are
attached to it
+ "localhost");
serverConnection.listen("/", new
AS2AsyncMDNServerManagerIT.RequestHandler());
}
}
diff --git
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBasicAuthHeaderTest.java
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBasicAuthHeaderTest.java
index 62d99d7caab6..8114e3d7476c 100644
---
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBasicAuthHeaderTest.java
+++
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBasicAuthHeaderTest.java
@@ -125,5 +125,8 @@ public class AS2ServerBasicAuthHeaderTest extends
AbstractAS2ITSupport {
@Override
protected void customizeConfiguration(AS2Configuration configuration) {
configuration.setServerPortNumber(TARGET_PORT);
+ // The MDN credentials are only attached to a delivery host the
operator has authorised, and the
+ // delivery address here comes from the receiptDeliveryOption header,
so localhost has to be listed.
+ configuration.setAsyncMdnAllowedHosts("localhost");
}
}
diff --git
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBearerAuthHeaderTest.java
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBearerAuthHeaderTest.java
index 6033e621f080..4d338a81279b 100644
---
a/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBearerAuthHeaderTest.java
+++
b/components/camel-as2/camel-as2-component/src/test/java/org/apache/camel/component/as2/AS2ServerBearerAuthHeaderTest.java
@@ -122,5 +122,8 @@ public class AS2ServerBearerAuthHeaderTest extends
AbstractAS2ITSupport {
@Override
protected void customizeConfiguration(AS2Configuration configuration) {
configuration.setServerPortNumber(TARGET_PORT);
+ // The MDN credentials are only attached to a delivery host the
operator has authorised, and the
+ // delivery address here comes from the receiptDeliveryOption header,
so localhost has to be listed.
+ configuration.setAsyncMdnAllowedHosts("localhost");
}
}
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
index 4cc6029c1070..efd829f154eb 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/AS2EndpointBuilderFactory.java
@@ -622,6 +622,27 @@ public interface AS2EndpointBuilderFactory {
doSetProperty("accessToken", accessToken);
return this;
}
+ /**
+ * Comma separated list of host names an asynchronous MDN may be
+ * delivered to. The delivery address is chosen by the sender of the
AS2
+ * message, so this is the list of hosts the operator vouches for. When
+ * set, an asynchronous MDN whose delivery address names a host outside
+ * the list is refused, and the configured
+ * mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
+ * the list. When not set, the MDN is still delivered to the
+ * sender-supplied address but no credentials are attached.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param asyncMdnAllowedHosts the value to set
+ * @return the dsl builder
+ */
+ default AS2EndpointConsumerBuilder asyncMdnAllowedHosts(String
asyncMdnAllowedHosts) {
+ doSetProperty("asyncMdnAllowedHosts", asyncMdnAllowedHosts);
+ return this;
+ }
/**
* The key used to encrypt the EDI message.
*
@@ -1653,6 +1674,27 @@ public interface AS2EndpointBuilderFactory {
doSetProperty("accessToken", accessToken);
return this;
}
+ /**
+ * Comma separated list of host names an asynchronous MDN may be
+ * delivered to. The delivery address is chosen by the sender of the
AS2
+ * message, so this is the list of hosts the operator vouches for. When
+ * set, an asynchronous MDN whose delivery address names a host outside
+ * the list is refused, and the configured
+ * mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
+ * the list. When not set, the MDN is still delivered to the
+ * sender-supplied address but no credentials are attached.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param asyncMdnAllowedHosts the value to set
+ * @return the dsl builder
+ */
+ default AS2EndpointProducerBuilder asyncMdnAllowedHosts(String
asyncMdnAllowedHosts) {
+ doSetProperty("asyncMdnAllowedHosts", asyncMdnAllowedHosts);
+ return this;
+ }
/**
* The key used to encrypt the EDI message.
*
@@ -2665,6 +2707,27 @@ public interface AS2EndpointBuilderFactory {
doSetProperty("accessToken", accessToken);
return this;
}
+ /**
+ * Comma separated list of host names an asynchronous MDN may be
+ * delivered to. The delivery address is chosen by the sender of the
AS2
+ * message, so this is the list of hosts the operator vouches for. When
+ * set, an asynchronous MDN whose delivery address names a host outside
+ * the list is refused, and the configured
+ * mdnUserName/mdnPassword/mdnAccessToken are attached only to a host
on
+ * the list. When not set, the MDN is still delivered to the
+ * sender-supplied address but no credentials are attached.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Group: security
+ *
+ * @param asyncMdnAllowedHosts the value to set
+ * @return the dsl builder
+ */
+ default AS2EndpointBuilder asyncMdnAllowedHosts(String
asyncMdnAllowedHosts) {
+ doSetProperty("asyncMdnAllowedHosts", asyncMdnAllowedHosts);
+ return this;
+ }
/**
* The key used to encrypt the EDI message.
*