Skip to content

Commit c2a83b8

Browse files
committed
[fix][proxy] Fix refresh client auth
Signed-off-by: Zixuan Liu <nodeces@gmail.com>
1 parent 08df28a commit c2a83b8

File tree

6 files changed

+381
-76
lines changed

6 files changed

+381
-76
lines changed

pulsar-client/src/main/java/org/apache/pulsar/client/impl/ClientCnx.java

+82-42
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.concurrent.Semaphore;
4545
import java.util.concurrent.TimeUnit;
4646
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
47+
import javax.naming.AuthenticationException;
4748
import lombok.AccessLevel;
4849
import lombok.Getter;
4950
import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -176,6 +177,9 @@ public class ClientCnx extends PulsarHandler {
176177
@Getter
177178
private final ClientCnxIdleState idleState;
178179

180+
@Getter
181+
private long lastDisconnectedTimestamp;
182+
179183
enum State {
180184
None, SentConnectFrame, Ready, Failed, Connecting
181185
}
@@ -253,34 +257,53 @@ public void channelActive(ChannelHandlerContext ctx) throws Exception {
253257
} else {
254258
log.info("{} Connected through proxy to target broker at {}", ctx.channel(), proxyToTargetBrokerAddress);
255259
}
256-
// Send CONNECT command
257-
ctx.writeAndFlush(newConnectCommand())
258-
.addListener(future -> {
259-
if (future.isSuccess()) {
260-
if (log.isDebugEnabled()) {
261-
log.debug("Complete: {}", future.isSuccess());
262-
}
263-
state = State.SentConnectFrame;
264-
} else {
265-
log.warn("Error during handshake", future.cause());
266-
ctx.close();
267-
}
268-
});
260+
handleChannelActive();
261+
}
262+
263+
protected String getOriginalAuthRole() {
264+
return null;
265+
}
266+
267+
protected CompletableFuture<AuthData> getOriginalAuthDataSupplier(boolean isRefresh) {
268+
return CompletableFuture.completedFuture(null);
269+
}
270+
271+
protected String getOriginalAuthMethod() {
272+
return null;
269273
}
270274

271-
protected ByteBuf newConnectCommand() throws Exception {
275+
protected void handleChannelActive() throws Exception {
272276
// mutual authentication is to auth between `remoteHostName` and this client for this channel.
273277
// each channel will have a mutual client/server pair, mutual client evaluateChallenge with init data,
274278
// and return authData to server.
275279
authenticationDataProvider = authentication.getAuthData(remoteHostName);
276280
AuthData authData = authenticationDataProvider.authenticate(AuthData.INIT_AUTH_DATA);
277-
return Commands.newConnect(authentication.getAuthMethodName(), authData, this.protocolVersion,
278-
PulsarVersion.getVersion(), proxyToTargetBrokerAddress, null, null, null);
281+
getOriginalAuthDataSupplier(false).thenAccept(originalAuthData -> {
282+
ByteBuf byteBuf = Commands.newConnect(authentication.getAuthMethodName(), authData, this.protocolVersion,
283+
PulsarVersion.getVersion(), proxyToTargetBrokerAddress, getOriginalAuthRole(), originalAuthData,
284+
getOriginalAuthMethod());
285+
ctx.writeAndFlush(byteBuf).addListener(future -> {
286+
if (future.isSuccess()) {
287+
if (log.isDebugEnabled()) {
288+
log.debug("Complete: {}", future.isSuccess());
289+
}
290+
state = State.SentConnectFrame;
291+
} else {
292+
closeWithException(future.cause());
293+
log.error("Error during handshake", future.cause());
294+
}
295+
});
296+
}).exceptionally(e -> {
297+
closeWithException(e);
298+
log.error("Error during handshake", e);
299+
return null;
300+
});
279301
}
280302

281303
@Override
282304
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
283305
super.channelInactive(ctx);
306+
lastDisconnectedTimestamp = System.currentTimeMillis();
284307
log.info("{} Disconnected", ctx.channel());
285308
if (!connectionFuture.isDone()) {
286309
connectionFuture.completeExceptionally(new PulsarClientException("Connection already closed"));
@@ -361,6 +384,19 @@ protected void handleConnected(CommandConnected connected) {
361384
state = State.Ready;
362385
}
363386

387+
protected CompletableFuture<AuthData> getMutualAuthData(CommandAuthChallenge authChallenge) {
388+
try {
389+
return CompletableFuture.completedFuture(
390+
authenticationDataProvider.authenticate(AuthData.of(authChallenge.getChallenge().getAuthData())));
391+
} catch (AuthenticationException e) {
392+
return FutureUtil.failedFuture(e);
393+
}
394+
}
395+
396+
protected String getMutualAuthMethod() {
397+
return authentication.getAuthMethodName();
398+
}
399+
364400
@Override
365401
protected void handleAuthChallenge(CommandAuthChallenge authChallenge) {
366402
checkArgument(authChallenge.hasChallenge());
@@ -371,42 +407,39 @@ protected void handleAuthChallenge(CommandAuthChallenge authChallenge) {
371407
authenticationDataProvider = authentication.getAuthData(remoteHostName);
372408
} catch (PulsarClientException e) {
373409
log.error("{} Error when refreshing authentication data provider: {}", ctx.channel(), e);
374-
connectionFuture.completeExceptionally(e);
410+
closeWithException(e);
375411
return;
376412
}
377413
}
378414

379-
// mutual authn. If auth not complete, continue auth; if auth complete, complete connectionFuture.
380415
try {
381-
AuthData authData = authenticationDataProvider
382-
.authenticate(AuthData.of(authChallenge.getChallenge().getAuthData()));
383-
384-
checkState(!authData.isComplete());
385-
386-
ByteBuf request = Commands.newAuthResponse(authentication.getAuthMethodName(),
387-
authData,
388-
this.protocolVersion,
389-
PulsarVersion.getVersion());
390-
391-
if (log.isDebugEnabled()) {
392-
log.debug("{} Mutual auth {}", ctx.channel(), authentication.getAuthMethodName());
393-
}
416+
getMutualAuthData(authChallenge).thenAccept(authData->{
417+
checkState(!authData.isComplete());
418+
String authMethod = getMutualAuthMethod();
419+
if (log.isDebugEnabled()) {
420+
log.debug("{} Mutual auth {}", ctx.channel(), authMethod);
421+
}
394422

395-
ctx.writeAndFlush(request).addListener(writeFuture -> {
396-
if (!writeFuture.isSuccess()) {
397-
log.warn("{} Failed to send request for mutual auth to broker: {}", ctx.channel(),
398-
writeFuture.cause().getMessage());
399-
connectionFuture.completeExceptionally(writeFuture.cause());
423+
ByteBuf request =
424+
Commands.newAuthResponse(authMethod, authData, protocolVersion, PulsarVersion.getVersion());
425+
ctx.writeAndFlush(request).addListener(writeFuture -> {
426+
if (!writeFuture.isSuccess()) {
427+
log.warn("{} Failed to send request for mutual auth to broker: {}", ctx.channel(),
428+
writeFuture.cause().getMessage());
429+
closeWithException(writeFuture.cause());
430+
}
431+
});
432+
if (state == State.SentConnectFrame) {
433+
state = State.Connecting;
400434
}
435+
}).exceptionally(e->{
436+
log.error("{} Error mutual verify: {}", ctx.channel(), e);
437+
closeWithException(e);
438+
return null;
401439
});
402-
403-
if (state == State.SentConnectFrame) {
404-
state = State.Connecting;
405-
}
406440
} catch (Exception e) {
407441
log.error("{} Error mutual verify: {}", ctx.channel(), e);
408-
connectionFuture.completeExceptionally(e);
409-
return;
442+
closeWithException(e);
410443
}
411444
}
412445

@@ -1243,6 +1276,13 @@ public void close() {
12431276
}
12441277
}
12451278

1279+
protected void closeWithException(Throwable e) {
1280+
if (ctx != null) {
1281+
ctx.close();
1282+
connectionFuture.completeExceptionally(e);
1283+
}
1284+
}
1285+
12461286
private void checkRequestTimeout() {
12471287
while (!requestTimeoutQueue.isEmpty()) {
12481288
RequestTime request = requestTimeoutQueue.peek();

pulsar-client/src/main/java/org/apache/pulsar/client/impl/ConnectionPool.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -453,5 +453,9 @@ public void doMarkAndReleaseUselessConnections(){
453453
// Do release idle connections.
454454
releaseIdleConnectionTaskList.forEach(Runnable::run);
455455
}
456-
}
457456

457+
@VisibleForTesting
458+
public ConcurrentMap<InetSocketAddress, ConcurrentMap<Integer, CompletableFuture<ClientCnx>>> getPool() {
459+
return pool;
460+
}
461+
}

pulsar-proxy/pom.xml

+5
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@
174174
<artifactId>ipaddress</artifactId>
175175
<version>${seancfoley.ipaddress.version}</version>
176176
</dependency>
177+
<dependency>
178+
<groupId>org.awaitility</groupId>
179+
<artifactId>awaitility</artifactId>
180+
<scope>test</scope>
181+
</dependency>
177182
</dependencies>
178183
<build>
179184
<plugins>

pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyClientCnx.java

+38-25
Original file line numberDiff line numberDiff line change
@@ -18,46 +18,59 @@
1818
*/
1919
package org.apache.pulsar.proxy.server;
2020

21-
import io.netty.buffer.ByteBuf;
2221
import io.netty.channel.EventLoopGroup;
23-
import org.apache.pulsar.PulsarVersion;
22+
import java.util.Arrays;
23+
import java.util.concurrent.CompletableFuture;
24+
import java.util.function.Function;
2425
import org.apache.pulsar.client.impl.ClientCnx;
2526
import org.apache.pulsar.client.impl.conf.ClientConfigurationData;
2627
import org.apache.pulsar.common.api.AuthData;
27-
import org.apache.pulsar.common.protocol.Commands;
28-
import org.slf4j.Logger;
29-
import org.slf4j.LoggerFactory;
28+
import org.apache.pulsar.common.api.proto.CommandAuthChallenge;
3029

3130
public class ProxyClientCnx extends ClientCnx {
31+
private final boolean forwardClientAuthData;
32+
private final String clientAuthMethod;
33+
private final String clientAuthRole;
34+
private final Function<Boolean, CompletableFuture<AuthData>> clientAuthDataSupplier;
3235

33-
String clientAuthRole;
34-
AuthData clientAuthData;
35-
String clientAuthMethod;
36-
int protocolVersion;
37-
36+
@Deprecated
3837
public ProxyClientCnx(ClientConfigurationData conf, EventLoopGroup eventLoopGroup, String clientAuthRole,
3938
AuthData clientAuthData, String clientAuthMethod, int protocolVersion) {
40-
super(conf, eventLoopGroup);
39+
this(conf, eventLoopGroup, clientAuthRole, (isRefresh) -> CompletableFuture.completedFuture(clientAuthData),
40+
clientAuthMethod, protocolVersion, clientAuthData != null);
41+
}
42+
43+
public ProxyClientCnx(ClientConfigurationData conf, EventLoopGroup eventLoopGroup, String clientAuthRole,
44+
Function<Boolean, CompletableFuture<AuthData>> clientAuthDataSupplier,
45+
String clientAuthMethod, int protocolVersion, boolean forwardClientAuthData) {
46+
super(conf, eventLoopGroup, protocolVersion);
4147
this.clientAuthRole = clientAuthRole;
42-
this.clientAuthData = clientAuthData;
48+
this.clientAuthDataSupplier = clientAuthDataSupplier;
4349
this.clientAuthMethod = clientAuthMethod;
44-
this.protocolVersion = protocolVersion;
50+
this.forwardClientAuthData = forwardClientAuthData;
4551
}
4652

4753
@Override
48-
protected ByteBuf newConnectCommand() throws Exception {
49-
if (log.isDebugEnabled()) {
50-
log.debug("New Connection opened via ProxyClientCnx with params clientAuthRole = {},"
51-
+ " clientAuthData = {}, clientAuthMethod = {}",
52-
clientAuthRole, clientAuthData, clientAuthMethod);
53-
}
54+
protected String getOriginalAuthRole() {
55+
return this.clientAuthRole;
56+
}
5457

55-
authenticationDataProvider = authentication.getAuthData(remoteHostName);
56-
AuthData authData = authenticationDataProvider.authenticate(AuthData.INIT_AUTH_DATA);
57-
return Commands.newConnect(authentication.getAuthMethodName(), authData, this.protocolVersion,
58-
PulsarVersion.getVersion(), proxyToTargetBrokerAddress, clientAuthRole, clientAuthData,
59-
clientAuthMethod);
58+
@Override
59+
protected String getOriginalAuthMethod() {
60+
return this.clientAuthMethod;
6061
}
6162

62-
private static final Logger log = LoggerFactory.getLogger(ProxyClientCnx.class);
63+
@Override
64+
protected CompletableFuture<AuthData> getOriginalAuthDataSupplier(boolean isRefresh) {
65+
return clientAuthDataSupplier.apply(isRefresh);
66+
}
67+
68+
@Override
69+
protected CompletableFuture<AuthData> getMutualAuthData(CommandAuthChallenge authChallenge) {
70+
boolean isRefresh = Arrays.equals(AuthData.REFRESH_AUTH_DATA_BYTES, authChallenge.getChallenge().getAuthData());
71+
if (!forwardClientAuthData || !isRefresh) {
72+
return super.getMutualAuthData(authChallenge);
73+
}
74+
return getOriginalAuthDataSupplier(true);
75+
}
6376
}

0 commit comments

Comments
 (0)