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 004809689f98 CAMEL-25708: camel-opensearch - bind the basic 
authentication credentials to the cluster nodes
004809689f98 is described below

commit 004809689f98d3c3ffd21a91e0ffd30555e87fc6
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Oct 16 16:58:09 2026 +0200

    CAMEL-25708: camel-opensearch - bind the basic authentication credentials 
to the cluster nodes
    
    [CAMEL-25708](https://issues.apache.org/jira/browse/CAMEL-25708): 
`OpensearchProducer.createClient()` registered the configured `user` and 
`password` with `new AuthScope(null, null, -1, null, null)`, which matches any 
host. [CAMEL-24815](https://issues.apache.org/jira/browse/CAMEL-24815) switched 
to it so that every node of a multi-host cluster receives the credentials, not 
only the first configured host.
    
    - The credentials are now held by a `NodeScopedCredentialsProvider`, which 
offers them only to the nodes the client talks to: the hosts of `hostAddresses` 
and, when `enableSniffer` is set, the nodes the sniffer finds. Every configured 
node keeps receiving them, as CAMEL-24815 intended.
    - A node matches on scheme, host name (in any case) and port. A node 
without a port matches the default port of its scheme.
    - The provider reads the nodes from the `RestClient` (`getNodes()`) on each 
lookup, so it follows the nodes the sniffer adds or removes.
    - A client set with the `client` or `openSearchClient` option is not 
changed.
    
    This is the same change as #27946 (CAMEL-25706) for camel-elasticsearch. 
Here it is written against HttpClient 5, whose `CredentialsProvider` only looks 
credentials up, and whose `AuthScope` carries the scheme (`getProtocol()`).
    
    Upgrade guide (4.24): a request to another host is sent without the 
credentials. This includes the host of a node with another scheme or port.
    
    Closes #27948
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../opensearch/NodeScopedCredentialsProvider.java  |  68 +++++++++++++
 .../component/opensearch/OpensearchProducer.java   |  37 +++++--
 .../NodeScopedCredentialsProviderTest.java         |  94 +++++++++++++++++
 .../OpensearchBasicAuthenticationTest.java         | 112 +++++++++++++++++++++
 .../OpensearchProducerCredentialsTest.java         |  82 +++++++++++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    |   8 ++
 6 files changed, 394 insertions(+), 7 deletions(-)

diff --git 
a/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProvider.java
 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProvider.java
new file mode 100644
index 000000000000..c03030907bfc
--- /dev/null
+++ 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.opensearch;
+
+import java.util.Collection;
+import java.util.function.Supplier;
+
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * Offers the configured credentials only to the nodes of the cluster the 
client talks to: the configured hosts, and the
+ * nodes found by the sniffer when it is enabled. A node matches when the 
scheme, the host name and the port are the
+ * same, the default port of the scheme standing in for a missing one.
+ */
+final class NodeScopedCredentialsProvider implements CredentialsProvider {
+
+    private final Credentials credentials;
+    private final Supplier<? extends Collection<HttpHost>> nodes;
+
+    NodeScopedCredentialsProvider(Credentials credentials, Supplier<? extends 
Collection<HttpHost>> nodes) {
+        this.credentials = credentials;
+        this.nodes = nodes;
+    }
+
+    @Override
+    public Credentials getCredentials(AuthScope authScope, HttpContext 
context) {
+        if (authScope != null && authScope.getProtocol() != null && 
authScope.getHost() != null) {
+            for (HttpHost node : nodes.get()) {
+                if (sameAuthority(node, authScope)) {
+                    return credentials;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static boolean sameAuthority(HttpHost node, AuthScope authScope) {
+        return node.getSchemeName().equalsIgnoreCase(authScope.getProtocol())
+                && node.getHostName().equalsIgnoreCase(authScope.getHost())
+                && effectivePort(node.getSchemeName(), node.getPort())
+                   == effectivePort(authScope.getProtocol(), 
authScope.getPort());
+    }
+
+    private static int effectivePort(String scheme, int port) {
+        if (port >= 0) {
+            return port;
+        }
+        return "https".equalsIgnoreCase(scheme) ? 443 : 80;
+    }
+}
diff --git 
a/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchProducer.java
 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchProducer.java
index 2d96335235bf..b490935cd064 100644
--- 
a/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchProducer.java
+++ 
b/components/camel-opensearch/src/main/java/org/apache/camel/component/opensearch/OpensearchProducer.java
@@ -22,7 +22,11 @@ import java.security.KeyStore;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.time.Duration;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
 
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManagerFactory;
@@ -37,16 +41,16 @@ import org.apache.camel.support.DefaultAsyncProducer;
 import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.util.IOHelper;
 import org.apache.camel.util.ObjectHelper;
-import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
 import org.apache.hc.client5.http.config.ConnectionConfig;
-import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
 import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
 import 
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
 import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.util.Timeout;
+import org.opensearch.client.Node;
 import org.opensearch.client.RestClient;
 import org.opensearch.client.RestClientBuilder;
 import org.opensearch.client.json.jackson.JacksonJsonpMapper;
@@ -495,17 +499,16 @@ class OpensearchProducer extends DefaultAsyncProducer {
 
     private RestClient createClient() {
         final RestClientBuilder builder = 
RestClient.builder(configuration.getHostAddressesList().toArray(new 
HttpHost[0]));
+        // the credentials provider reads the nodes from the client, as the 
sniffer can change them
+        final AtomicReference<RestClient> restClientRef = new 
AtomicReference<>();
 
         builder.setRequestConfigCallback(requestConfigBuilder -> 
requestConfigBuilder
                 
.setConnectTimeout(Timeout.of(Duration.ofMillis(configuration.getConnectionTimeout())))
                 // apply the socket/read timeout for both plain-HTTP and SSL 
connections, not only when SSL is enabled
                 
.setResponseTimeout(Timeout.of(Duration.ofMillis(configuration.getSocketTimeout()))));
         builder.setHttpClientConfigCallback(httpClientBuilder -> {
-            if (ObjectHelper.isNotEmpty(configuration.getUser()) && 
ObjectHelper.isNotEmpty(configuration.getPassword())) {
-                final BasicCredentialsProvider credentialsProvider = new 
BasicCredentialsProvider();
-                // match-all AuthScope so basic auth is sent to every node, 
not only the first configured host
-                credentialsProvider.setCredentials(new AuthScope(null, null, 
-1, null, null),
-                        new 
UsernamePasswordCredentials(configuration.getUser(), 
configuration.getPassword().toCharArray()));
+            CredentialsProvider credentialsProvider = 
createCredentialsProvider(() -> nodeHosts(restClientRef.get()));
+            if (credentialsProvider != null) {
                 
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
             }
 
@@ -558,6 +561,7 @@ class OpensearchProducer extends DefaultAsyncProducer {
         });
 
         final RestClient restClient = builder.build();
+        restClientRef.set(restClient);
         if (configuration.isEnableSniffer()) {
             SnifferBuilder snifferBuilder = Sniffer.builder(restClient);
             
snifferBuilder.setSniffIntervalMillis(configuration.getSnifferInterval());
@@ -567,6 +571,25 @@ class OpensearchProducer extends DefaultAsyncProducer {
         return restClient;
     }
 
+    /**
+     * Creates the provider of the configured user and password, which offers 
them only to the given nodes - every node
+     * of the cluster, not only the first configured host - or returns null 
when no credentials are configured.
+     */
+    CredentialsProvider createCredentialsProvider(Supplier<? extends 
Collection<HttpHost>> nodes) {
+        if (ObjectHelper.isEmpty(configuration.getUser()) || 
ObjectHelper.isEmpty(configuration.getPassword())) {
+            return null;
+        }
+        return new NodeScopedCredentialsProvider(
+                new UsernamePasswordCredentials(configuration.getUser(), 
configuration.getPassword().toCharArray()), nodes);
+    }
+
+    private List<HttpHost> nodeHosts(RestClient restClient) {
+        if (restClient == null) {
+            return configuration.getHostAddressesList();
+        }
+        return restClient.getNodes().stream().map(Node::getHost).toList();
+    }
+
     @Override
     protected void doStop() throws Exception {
         if (client != null && openSearchClient == null) {
diff --git 
a/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProviderTest.java
 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProviderTest.java
new file mode 100644
index 000000000000..2f2df73ed3e3
--- /dev/null
+++ 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/NodeScopedCredentialsProviderTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.opensearch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.core5.http.HttpHost;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+class NodeScopedCredentialsProviderTest {
+
+    private static final Credentials CREDENTIALS = new 
UsernamePasswordCredentials("admin", "secret".toCharArray());
+
+    private final List<HttpHost> nodes = new ArrayList<>(
+            List.of(new HttpHost("http", "os1.example.com", 9200),
+                    new HttpHost("https", "os2.example.com", 9243),
+                    new HttpHost("https", "os3.example.com", -1)));
+
+    private final NodeScopedCredentialsProvider provider = new 
NodeScopedCredentialsProvider(CREDENTIALS, () -> nodes);
+
+    @Test
+    void offersTheCredentialsToEveryNode() {
+        assertSame(CREDENTIALS, credentialsFor("http", "os1.example.com", 
9200));
+        assertSame(CREDENTIALS, credentialsFor("https", "os2.example.com", 
9243));
+    }
+
+    @Test
+    void usesTheDefaultPortOfTheSchemeWhenTheNodeHasNone() {
+        assertSame(CREDENTIALS, credentialsFor("https", "os3.example.com", 
443));
+        assertNull(credentialsFor("https", "os3.example.com", 9200));
+    }
+
+    @Test
+    void matchesTheHostNameRegardlessOfCase() {
+        assertSame(CREDENTIALS, credentialsFor("HTTP", "OS1.Example.COM", 
9200));
+    }
+
+    @Test
+    void offersNoCredentialsToAHostThatIsNotANode() {
+        assertNull(credentialsFor("http", "other.example.com", 9200));
+    }
+
+    @Test
+    void offersNoCredentialsToAnotherPortOrSchemeOfANode() {
+        assertNull(credentialsFor("http", "os1.example.com", 9300));
+        assertNull(credentialsFor("https", "os1.example.com", 9200));
+        assertNull(credentialsFor("http", "os2.example.com", 9243));
+    }
+
+    @Test
+    void followsTheCurrentNodes() {
+        HttpHost sniffed = new HttpHost("http", "os4.example.com", 9200);
+        assertNull(credentialsFor("http", "os4.example.com", 9200));
+
+        nodes.add(sniffed);
+        assertSame(CREDENTIALS, credentialsFor("http", "os4.example.com", 
9200));
+
+        nodes.remove(sniffed);
+        assertNull(credentialsFor("http", "os4.example.com", 9200));
+    }
+
+    @Test
+    void offersNoCredentialsToAScopeWithoutProtocolOrHost() {
+        assertNull(provider.getCredentials(new AuthScope(null, null, -1, null, 
null), null));
+        assertNull(provider.getCredentials(new AuthScope("os1.example.com", 
9200), null));
+        assertNull(provider.getCredentials(null, null));
+    }
+
+    private Credentials credentialsFor(String scheme, String host, int port) {
+        // the shape of the scope HttpClient looks the credentials up with, 
preemptively or after a challenge
+        return provider.getCredentials(new AuthScope(new HttpHost(scheme, 
host, port), "realm", "Basic"), null);
+    }
+}
diff --git 
a/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchBasicAuthenticationTest.java
 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchBasicAuthenticationTest.java
new file mode 100644
index 000000000000..2b99695c56ec
--- /dev/null
+++ 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchBasicAuthenticationTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.opensearch;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import com.sun.net.httpserver.HttpServer;
+import org.apache.camel.CamelContext;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * The producer authenticates to every node it is configured with, through the 
client it creates itself.
+ */
+class OpensearchBasicAuthenticationTest {
+
+    private static final String AUTHORIZATION
+            = "Basic " + 
Base64.getEncoder().encodeToString("admin:secret".getBytes(StandardCharsets.UTF_8));
+
+    private final ClusterNode node1 = new ClusterNode();
+    private final ClusterNode node2 = new ClusterNode();
+
+    @BeforeEach
+    void startNodes() throws IOException {
+        node1.start();
+        node2.start();
+    }
+
+    @AfterEach
+    void stopNodes() {
+        node1.stop();
+        node2.stop();
+    }
+
+    @Test
+    void sendsTheConfiguredCredentialsToEveryNode() throws Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            OpensearchComponent component = new OpensearchComponent(context);
+            component.setHostAddresses(node1.address() + "," + 
node2.address());
+            component.setUser("admin");
+            component.setPassword("secret");
+            context.addComponent("opensearch", component);
+            context.start();
+
+            // the client sends the requests to the nodes in turn
+            ProducerTemplate template = context.createProducerTemplate();
+            assertEquals(Boolean.TRUE, 
template.requestBody("opensearch:test-cluster?operation=Ping", (Object) null));
+            assertEquals(Boolean.TRUE, 
template.requestBody("opensearch:test-cluster?operation=Ping", (Object) null));
+
+            assertEquals(List.of(AUTHORIZATION), node1.authorizations);
+            assertEquals(List.of(AUTHORIZATION), node2.authorizations);
+        }
+    }
+
+    /**
+     * A node that answers a ping with the configured credentials, and asks 
for them otherwise.
+     */
+    private static final class ClusterNode {
+
+        private final List<String> authorizations = new 
CopyOnWriteArrayList<>();
+        private HttpServer server;
+
+        void start() throws IOException {
+            server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 
0);
+            server.createContext("/", exchange -> {
+                exchange.getRequestBody().readAllBytes();
+                String authorization = 
exchange.getRequestHeaders().getFirst("Authorization");
+                authorizations.add(String.valueOf(authorization));
+                if (AUTHORIZATION.equals(authorization)) {
+                    exchange.sendResponseHeaders(200, -1);
+                } else {
+                    exchange.getResponseHeaders().add("WWW-Authenticate", 
"Basic realm=\"camel\"");
+                    exchange.sendResponseHeaders(401, -1);
+                }
+                exchange.close();
+            });
+            server.start();
+        }
+
+        String address() {
+            return "127.0.0.1:" + server.getAddress().getPort();
+        }
+
+        void stop() {
+            server.stop(0);
+        }
+    }
+}
diff --git 
a/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchProducerCredentialsTest.java
 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchProducerCredentialsTest.java
new file mode 100644
index 000000000000..1f89865ca829
--- /dev/null
+++ 
b/components/camel-opensearch/src/test/java/org/apache/camel/component/opensearch/OpensearchProducerCredentialsTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.opensearch;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.core5.http.HttpHost;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+class OpensearchProducerCredentialsTest {
+
+    @Test
+    void offersTheConfiguredCredentialsToEveryConfiguredHostOnly() throws 
Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.start();
+            OpensearchConfiguration configuration
+                    = configuration(context, 
"os1.example.com:9200,os2.example.com:9201", "admin", "secret");
+
+            CredentialsProvider provider = new 
OpensearchProducer(endpoint(context), configuration)
+                    
.createCredentialsProvider(configuration::getHostAddressesList);
+
+            Credentials credentials = provider.getCredentials(scope("http", 
"os1.example.com", 9200), null);
+            assertNotNull(credentials);
+            assertEquals("admin", credentials.getUserPrincipal().getName());
+            assertNotNull(provider.getCredentials(scope("http", 
"os2.example.com", 9201), null));
+
+            assertNull(provider.getCredentials(scope("http", 
"other.example.com", 9200), null));
+            assertNull(provider.getCredentials(scope("http", 
"os1.example.com", 9201), null));
+            assertNull(provider.getCredentials(scope("https", 
"os1.example.com", 9200), null));
+        }
+    }
+
+    @Test
+    void createsNoProviderWithoutCredentials() throws Exception {
+        try (CamelContext context = new DefaultCamelContext()) {
+            context.start();
+            OpensearchConfiguration configuration = configuration(context, 
"os1.example.com:9200", null, null);
+
+            assertNull(new OpensearchProducer(endpoint(context), configuration)
+                    
.createCredentialsProvider(configuration::getHostAddressesList));
+        }
+    }
+
+    private static OpensearchConfiguration configuration(
+            CamelContext context, String hostAddresses, String user, String 
password) {
+        OpensearchComponent component = new OpensearchComponent(context);
+        component.setHostAddresses(hostAddresses);
+        component.setUser(user);
+        component.setPassword(password);
+        context.addComponent("opensearch", component);
+        return endpoint(context).getConfiguration();
+    }
+
+    private static OpensearchEndpoint endpoint(CamelContext context) {
+        return context.getEndpoint("opensearch:test-cluster", 
OpensearchEndpoint.class);
+    }
+
+    private static AuthScope scope(String scheme, String host, int port) {
+        return new AuthScope(new HttpHost(scheme, host, port), "realm", 
"Basic");
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index f2a3d05637c4..f24b85469360 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -811,6 +811,14 @@ older entry was updated after newer ones were published.
 Routes using `sortEntries=true` with `idempotent=false` get the entries in 
this order too: the entry that was updated
 first is routed first, where the entry that was published last was routed 
first before.
 
+=== camel-opensearch - the credentials are only sent to the nodes of the 
cluster
+
+The `user` and `password` are now offered only to the nodes the client talks 
to: the hosts of `hostAddresses` and,
+when `enableSniffer` is set, the nodes the sniffer finds, matching the scheme, 
host and port. Every node of a
+multi-host cluster still receives them. Previously they were registered for 
any host. A request to another host,
+including the host of a node with another scheme or port, is now sent without 
them. A client set with the `client` or
+`openSearchClient` option is not changed.
+
 === camel-aws2-s3 - a listed object that cannot be fetched is skipped
 
 When the consumer lists the bucket, a failure to get one of the listed objects 
(or to download its content) no longer

Reply via email to