diff --git a/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapter.java b/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapter.java index 6b4c6ba38d3b5..bb51d2f9107e6 100644 --- a/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapter.java +++ b/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapter.java @@ -18,8 +18,12 @@ */ package org.apache.pulsar.client.impl.v5; +import java.security.PrivateKey; +import java.security.cert.Certificate; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; +import org.apache.pulsar.client.api.AuthenticationDataProvider; import org.apache.pulsar.client.api.v5.PulsarClientException; import org.apache.pulsar.client.api.v5.auth.Authentication; import org.apache.pulsar.client.api.v5.auth.AuthenticationData; @@ -107,8 +111,22 @@ public String authMethodName() { @Override public AuthenticationData authData() throws PulsarClientException { - // TODO: Implement AuthenticationData adapter when needed - return null; + try { + AuthenticationDataProvider v4Data = v4Auth.getAuthData(); + return v4Data == null ? null : new V5AuthDataWrapper(v4Data); + } catch (org.apache.pulsar.client.api.PulsarClientException e) { + throw new PulsarClientException(e.getMessage(), e); + } + } + + @Override + public AuthenticationData authData(String brokerHostName) throws PulsarClientException { + try { + AuthenticationDataProvider v4Data = v4Auth.getAuthData(brokerHostName); + return v4Data == null ? null : new V5AuthDataWrapper(v4Data); + } catch (org.apache.pulsar.client.api.PulsarClientException e) { + throw new PulsarClientException(e.getMessage(), e); + } } @Override @@ -129,4 +147,56 @@ public void close() { } } } + + /** + * Bridges a v4 {@link AuthenticationDataProvider} into the V5 {@link AuthenticationData} + * surface. The two interfaces are intentionally aligned so this is a 1:1 delegation. + */ + private static final class V5AuthDataWrapper implements AuthenticationData { + private final AuthenticationDataProvider v4Data; + + V5AuthDataWrapper(AuthenticationDataProvider v4Data) { + this.v4Data = v4Data; + } + + @Override + public boolean hasDataForHttp() { + return v4Data.hasDataForHttp(); + } + + @Override + public Set> getHttpHeaders() { + try { + Set> headers = v4Data.getHttpHeaders(); + return headers != null ? headers : Set.of(); + } catch (Exception e) { + throw new RuntimeException("Failed to obtain HTTP auth headers", e); + } + } + + @Override + public boolean hasDataForTls() { + return v4Data.hasDataForTls(); + } + + @Override + public Certificate[] getTlsCertificates() { + return v4Data.getTlsCertificates(); + } + + @Override + public PrivateKey getTlsPrivateKey() { + return v4Data.getTlsPrivateKey(); + } + + @Override + public boolean hasDataFromCommand() { + return v4Data.hasDataFromCommand(); + } + + @Override + public String getCommandData() { + return v4Data.getCommandData(); + } + } } diff --git a/pulsar-client-v5/src/test/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapterTest.java b/pulsar-client-v5/src/test/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapterTest.java new file mode 100644 index 0000000000000..283cff17b157d --- /dev/null +++ b/pulsar-client-v5/src/test/java/org/apache/pulsar/client/impl/v5/AuthenticationAdapterTest.java @@ -0,0 +1,62 @@ +/* + * 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.pulsar.client.impl.v5; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import org.apache.pulsar.client.api.v5.auth.Authentication; +import org.apache.pulsar.client.api.v5.auth.AuthenticationData; +import org.testng.annotations.Test; + +/** + * Verifies that {@code AuthenticationAdapter}'s V5 wrapper bridges through to the + * v4 {@code AuthenticationDataProvider} on every dimension exposed by the V5 + * {@link AuthenticationData} surface — previously {@code authData()} was a TODO + * stub returning {@code null}, which silently broke any V5 caller that asked the + * wrapped auth for credentials. + */ +public class AuthenticationAdapterTest { + + @Test + public void testTokenAuthExposesCommandData() throws Exception { + Authentication v5Auth = AuthenticationAdapter.token("my-jwt"); + + AuthenticationData data = v5Auth.authData(); + assertNotNull(data, "authData() must not return null after the wiring fix"); + assertTrue(data.hasDataFromCommand(), + "token auth should expose its credential via the binary-protocol command path"); + assertEquals(data.getCommandData(), "my-jwt"); + } + + @Test + public void testAuthMethodNamePassesThrough() throws Exception { + Authentication v5Auth = AuthenticationAdapter.token("any-token"); + assertEquals(v5Auth.authMethodName(), "token"); + } + + @Test + public void testAuthDataPerBrokerHostDelegates() throws Exception { + Authentication v5Auth = AuthenticationAdapter.token("per-host-token"); + + AuthenticationData data = v5Auth.authData("broker-1.example.com"); + assertNotNull(data, "authData(brokerHost) must not return null"); + assertEquals(data.getCommandData(), "per-host-token"); + } +}