This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 180eebf592c7 CAMEL-26169: camel-sap-netweaver - keep the url query 
string and support special characters in the credentials (#28945)
180eebf592c7 is described below

commit 180eebf592c7197b8d7c1507d902c8a245498b6d
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Nov 2 21:15:11 2026 +0100

    CAMEL-26169: camel-sap-netweaver - keep the url query string and support 
special characters in the credentials (#28945)
    
    * CAMEL-26169: camel-sap-netweaver - keep the url query string and supp…
    
    …ort special characters in the credentials
    
    The producer built the http endpoint uri as url + "?authUsername=...", so a 
url
    that already had query parameters got a second "?" and the credentials 
ended up
    inside the last url parameter (the server answered 401). The username and
    password were also added without RAW(), so a password with & was cut there 
and
    a + became a space.
    
    Now the auth parameters are appended with & when the url already has a 
query,
    and the username and password are wrapped in RAW().
    
    * CAMEL-26169: camel-sap-netweaver - use RAW{...} for credentials that …
    
    …contain a )
    
    RAW(...) ends at a ) followed by &, so a password such as a)&b=c was cut
    short and the rest leaked into the http query. A credential that contains
    a ) is now passed on with the RAW{...} form.
    
    * CAMEL-26169: camel-sap-netweaver - document the RAW syntax corner case
    
    A credential with both a ) followed by & and a } fits neither RAW form,
    note it in a code comment (review feedback).
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 components/camel-sap-netweaver/pom.xml             |   7 ++
 .../component/sap/netweaver/NetWeaverProducer.java |  17 ++-
 .../sap/netweaver/NetWeaverProducerTest.java       | 118 +++++++++++++++++++++
 3 files changed, 140 insertions(+), 2 deletions(-)

diff --git a/components/camel-sap-netweaver/pom.xml 
b/components/camel-sap-netweaver/pom.xml
index 3c5778f8670a..9bfea473c386 100644
--- a/components/camel-sap-netweaver/pom.xml
+++ b/components/camel-sap-netweaver/pom.xml
@@ -74,6 +74,11 @@
             <artifactId>camel-velocity</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -84,6 +89,8 @@
                     <includes>
                         <!-- Here we only run test of  -->
                         <include>**/*UriConfigurationTest.java</include>
+                        <!-- uses a local http server -->
+                        <include>**/NetWeaverProducerTest.java</include>
                     </includes>
                 </configuration>
             </plugin>
diff --git 
a/components/camel-sap-netweaver/src/main/java/org/apache/camel/component/sap/netweaver/NetWeaverProducer.java
 
b/components/camel-sap-netweaver/src/main/java/org/apache/camel/component/sap/netweaver/NetWeaverProducer.java
index 9ff7fa8cd3d0..e4669c805e5b 100644
--- 
a/components/camel-sap-netweaver/src/main/java/org/apache/camel/component/sap/netweaver/NetWeaverProducer.java
+++ 
b/components/camel-sap-netweaver/src/main/java/org/apache/camel/component/sap/netweaver/NetWeaverProducer.java
@@ -78,8 +78,11 @@ public class NetWeaverProducer extends DefaultProducer {
 
     @Override
     protected void doStart() throws Exception {
-        String url = getEndpoint().getUrl() + "?authUsername=" + 
getEndpoint().getUsername() + "&authPassword="
-                     + getEndpoint().getPassword() + "&authMethod=Basic";
+        String url = getEndpoint().getUrl();
+        // the url may already have query parameters, and the credentials use 
RAW so characters such as & and + are kept
+        url += (url.indexOf('?') != -1 ? "&" : "?")
+               + "authUsername=" + raw(getEndpoint().getUsername()) + 
"&authPassword=" + raw(getEndpoint().getPassword())
+               + "&authMethod=Basic";
         if (LOG.isInfoEnabled()) {
             LOG.info("Creating NetWeaverProducer using url: {}", 
URISupport.sanitizeUri(url));
         }
@@ -88,6 +91,16 @@ public class NetWeaverProducer extends DefaultProducer {
         ServiceHelper.startService(http);
     }
 
+    private static String raw(String value) {
+        // RAW(...) ends at a ) followed by &, so a value with a ) uses the 
RAW{...} form instead
+        // a value with both a ) followed by & and a } fits neither RAW form 
(a limit of the RAW syntax),
+        // and is cut short at the )&
+        if (value != null && value.indexOf(')') != -1 && value.indexOf('}') == 
-1) {
+            return "RAW{" + value + "}";
+        }
+        return "RAW(" + value + ")";
+    }
+
     @Override
     protected void doStop() throws Exception {
         ServiceHelper.stopService(http);
diff --git 
a/components/camel-sap-netweaver/src/test/java/org/apache/camel/component/sap/netweaver/NetWeaverProducerTest.java
 
b/components/camel-sap-netweaver/src/test/java/org/apache/camel/component/sap/netweaver/NetWeaverProducerTest.java
new file mode 100644
index 000000000000..18b5872007d3
--- /dev/null
+++ 
b/components/camel-sap-netweaver/src/test/java/org/apache/camel/component/sap/netweaver/NetWeaverProducerTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.sap.netweaver;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class NetWeaverProducerTest extends CamelTestSupport {
+
+    private HttpServer server;
+    private volatile String credentials;
+    private volatile URI requestUri;
+
+    @Override
+    public void setupResources() throws Exception {
+        server = ServerBootstrap.bootstrap()
+                .setCanonicalHostName("localhost")
+                .register("*", (request, response, context) -> {
+                    Header auth = 
request.getFirstHeader(HttpHeaders.AUTHORIZATION);
+                    if (auth == null) {
+                        // ask for basic authentication, so the client sends 
the credentials
+                        response.setCode(HttpStatus.SC_UNAUTHORIZED);
+                        response.setHeader(HttpHeaders.WWW_AUTHENTICATE, 
"Basic realm=\"netweaver\"");
+                        return;
+                    }
+                    credentials = new String(
+                            
Base64.getDecoder().decode(auth.getValue().substring("Basic ".length())), 
StandardCharsets.UTF_8);
+                    try {
+                        requestUri = request.getUri();
+                    } catch (Exception e) {
+                        throw new IllegalStateException(e);
+                    }
+                    response.setCode(HttpStatus.SC_OK);
+                    response.setEntity(new 
StringEntity("{\"d\":{\"Name\":\"Camel\"}}", ContentType.APPLICATION_JSON));
+                })
+                .create();
+        server.start();
+    }
+
+    @Override
+    public void cleanupResources() {
+        if (server != null) {
+            server.stop();
+        }
+    }
+
+    @Test
+    public void testUrlWithQueryParameters() {
+        String base = "http://localhost:"; + server.getLocalPort() + "/odata";
+        NetWeaverEndpoint endpoint = context.getEndpoint(
+                "sap-netweaver:" + base + "?username=camel&password=secret", 
NetWeaverEndpoint.class);
+        endpoint.setUrl(base + "?sap-client=100");
+
+        Object body = template.requestBodyAndHeader(endpoint, null, 
NetWeaverConstants.COMMAND, "Flights");
+
+        assertThat(body).isEqualTo(Map.of("Name", "Camel"));
+        assertThat(requestUri.getPath()).isEqualTo("/odata/Flights");
+        assertThat(requestUri.getQuery()).isEqualTo("sap-client=100");
+        assertThat(credentials).isEqualTo("camel:secret");
+    }
+
+    @Test
+    public void testPasswordWithSpecialCharacters() {
+        String base = "http://localhost:"; + server.getLocalPort() + "/odata";
+
+        Object body = template.requestBodyAndHeader(
+                "sap-netweaver:" + base + 
"?username=camel&password=RAW(a&b=c+d)", null, NetWeaverConstants.COMMAND,
+                "Flights");
+
+        assertThat(body).isEqualTo(Map.of("Name", "Camel"));
+        assertThat(requestUri.getPath()).isEqualTo("/odata/Flights");
+        assertThat(requestUri.getQuery()).isNull();
+        assertThat(credentials).isEqualTo("camel:a&b=c+d");
+    }
+
+    @Test
+    public void testPasswordWithClosingParenthesis() {
+        String base = "http://localhost:"; + server.getLocalPort() + "/odata";
+        NetWeaverEndpoint endpoint = context.getEndpoint(
+                "sap-netweaver:" + base + "?username=camel&password=secret", 
NetWeaverEndpoint.class);
+        // RAW(...) would end at the ) followed by &, so such a password is 
passed on with RAW{...}
+        endpoint.setPassword("a)&b=c)");
+
+        Object body = template.requestBodyAndHeader(endpoint, null, 
NetWeaverConstants.COMMAND, "Flights");
+
+        assertThat(body).isEqualTo(Map.of("Name", "Camel"));
+        assertThat(requestUri.getQuery()).isNull();
+        assertThat(credentials).isEqualTo("camel:a)&b=c)");
+    }
+}

Reply via email to