Skip to content

Commit cf17ae2

Browse files
committed
JVMCBC-1687 DelegatingAuthenticator does not work with Protostellar
Motivation ---------- Every time the CallCredentials authenticates a gRPC connection, it should get a fresh Authorization header value from the authenticator. Modifications ------------- Add Authenticator.getAuthHeaderValue(), which returns a value to use for an `Authorization` header for simple HTTP requests as well as gRPC requests. Add a default implementation for Authenticator.authHttpRequest(), so this code can be shared by PasswordAuthenticator and the forthcoming JwtAuthenticator. Remove Authenticator.protostellarCallCredentials(). Instead, ProtostellarEndpoint builds the CallCredentials using an `Authorization` header value dynamically fetched from the new Authenticator.getAuthHeaderValue() method. Change-Id: I34d9c02bf00f80418b001a94ab93b5483bf1bf05 Reviewed-on: https://review.couchbase.org/c/couchbase-jvm-clients/+/233963 Reviewed-by: Michael Reiche <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent 65f1ad3 commit cf17ae2

File tree

5 files changed

+43
-61
lines changed

5 files changed

+43
-61
lines changed

core-io/src/main/java/com/couchbase/client/core/endpoint/ProtostellarEndpoint.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import com.couchbase.client.core.deps.io.grpc.netty.GrpcSslContexts;
3939
import com.couchbase.client.core.deps.io.grpc.netty.NettyChannelBuilder;
4040
import com.couchbase.client.core.deps.io.netty.channel.ChannelOption;
41-
import com.couchbase.client.core.diagnostics.AuthenticationStatus;
4241
import com.couchbase.client.core.deps.io.netty.handler.ssl.SslContext;
4342
import com.couchbase.client.core.diagnostics.ClusterState;
4443
import com.couchbase.client.core.diagnostics.EndpointDiagnostics;
@@ -73,9 +72,12 @@
7372
import java.util.Optional;
7473
import java.util.Set;
7574
import java.util.concurrent.CompletableFuture;
75+
import java.util.concurrent.Executor;
7676
import java.util.concurrent.TimeUnit;
7777
import java.util.concurrent.atomic.AtomicBoolean;
78+
import java.util.function.Supplier;
7879

80+
import static com.couchbase.client.core.deps.io.grpc.Metadata.ASCII_STRING_MARSHALLER;
7981
import static java.util.Objects.requireNonNull;
8082

8183
/**
@@ -121,7 +123,7 @@ public ProtostellarEndpoint(ProtostellarContext ctx, HostAndPort remote) {
121123
ConnectivityState now = this.managedChannel.getState(false);
122124
notifyOnChannelStateChange(now);
123125

124-
CallCredentials creds = ctx.authenticator().protostellarCallCredentials();
126+
CallCredentials creds = callCredentials(() -> ctx.authenticator().getAuthHeaderValue());
125127

126128
// JVMCBC-1187: Temporary code to provide some insight on GRPC internals, will likely be removed pre-GA.
127129
ClientStreamTracer.Factory factory = new ClientStreamTracer.Factory() {
@@ -203,6 +205,27 @@ public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT
203205
searchAdminStub = SearchAdminServiceGrpc.newFutureStub(managedChannel).withCallCredentials(creds);
204206
}
205207

208+
private static CallCredentials callCredentials(Supplier<String> authHeaderValue) {
209+
return new CallCredentials() {
210+
@Override
211+
public void applyRequestMetadata(RequestInfo requestInfo, Executor executor, MetadataApplier applier) {
212+
executor.execute(() -> {
213+
try {
214+
Metadata headers = new Metadata();
215+
String value = authHeaderValue.get();
216+
if (value == null) {
217+
throw new FeatureNotAvailableException("This Authenticator is not compatible with couchbase2");
218+
}
219+
headers.put(Metadata.Key.of("Authorization", ASCII_STRING_MARSHALLER), value);
220+
applier.apply(headers);
221+
} catch (Throwable e) {
222+
applier.fail(Status.UNAUTHENTICATED.withCause(e));
223+
}
224+
});
225+
}
226+
};
227+
}
228+
206229
private ManagedChannel channel(ProtostellarContext ctx) {
207230
SecurityConfig securityConfig = ctx.environment().securityConfig();
208231

core-io/src/main/java/com/couchbase/client/core/env/Authenticator.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package com.couchbase.client.core.env;
1818

1919
import com.couchbase.client.core.annotation.Stability;
20-
import com.couchbase.client.core.deps.io.grpc.CallCredentials;
2120
import com.couchbase.client.core.deps.io.netty.channel.ChannelPipeline;
21+
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderNames;
2222
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpRequest;
2323
import com.couchbase.client.core.deps.io.netty.handler.ssl.SslContextBuilder;
2424
import com.couchbase.client.core.endpoint.EndpointContext;
@@ -56,11 +56,21 @@ default void authKeyValueConnection(final EndpointContext endpointContext, final
5656
* @param request the http request.
5757
*/
5858
@Stability.Internal
59-
default void authHttpRequest(final ServiceType serviceType, final HttpRequest request) { }
59+
default void authHttpRequest(final ServiceType serviceType, final HttpRequest request) {
60+
String authHeaderValue = getAuthHeaderValue();
61+
if (authHeaderValue != null) {
62+
request.headers().add(HttpHeaderNames.AUTHORIZATION, authHeaderValue);
63+
}
64+
}
6065

61-
@Nullable
66+
/**
67+
* Returns the value of the Authorization header to apply to HTTP requests,
68+
* or null if no header should be applied.
69+
*/
6270
@Stability.Internal
63-
CallCredentials protostellarCallCredentials();
71+
default @Nullable String getAuthHeaderValue() {
72+
return null;
73+
}
6474

6575
/**
6676
* The authenticator gets the chance to attach the client certificate to the ssl context if needed.

core-io/src/main/java/com/couchbase/client/core/env/AuthenticatorWrapper.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package com.couchbase.client.core.env;
1818

1919
import com.couchbase.client.core.annotation.Stability;
20-
import com.couchbase.client.core.deps.io.grpc.CallCredentials;
2120
import com.couchbase.client.core.deps.io.netty.channel.ChannelPipeline;
2221
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpRequest;
2322
import com.couchbase.client.core.deps.io.netty.handler.ssl.SslContextBuilder;
@@ -43,8 +42,8 @@ public void authHttpRequest(final ServiceType serviceType, final HttpRequest req
4342
}
4443

4544
@Override
46-
public @Nullable CallCredentials protostellarCallCredentials() {
47-
return wrapped().protostellarCallCredentials();
45+
public @Nullable String getAuthHeaderValue() {
46+
return wrapped().getAuthHeaderValue();
4847
}
4948

5049
@Override

core-io/src/main/java/com/couchbase/client/core/env/CertificateAuthenticator.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@
1616

1717
package com.couchbase.client.core.env;
1818

19-
import com.couchbase.client.core.annotation.Stability;
20-
import com.couchbase.client.core.deps.io.grpc.CallCredentials;
2119
import com.couchbase.client.core.deps.io.netty.handler.ssl.SslContextBuilder;
22-
import com.couchbase.client.core.error.FeatureNotAvailableException;
2320
import com.couchbase.client.core.error.InvalidArgumentException;
24-
import reactor.util.annotation.Nullable;
2521

2622
import javax.net.ssl.KeyManagerFactory;
2723
import java.io.InputStream;
@@ -134,14 +130,6 @@ private CertificateAuthenticator(final PrivateKey key, final String keyPassword,
134130
}
135131
}
136132

137-
@Override
138-
@Nullable
139-
@Stability.Internal
140-
public CallCredentials protostellarCallCredentials() {
141-
// To be added under JVMCBC-1195
142-
throw new FeatureNotAvailableException("CertificateAuthenticator is not supported with couchbase2");
143-
}
144-
145133
@Override
146134
public void applyTlsProperties(final SslContextBuilder context) {
147135
if (keyManagerFactory != null) {

core-io/src/main/java/com/couchbase/client/core/env/PasswordAuthenticator.java

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,18 @@
1616
package com.couchbase.client.core.env;
1717

1818
import com.couchbase.client.core.annotation.Stability;
19-
import com.couchbase.client.core.deps.io.grpc.CallCredentials;
20-
import com.couchbase.client.core.deps.io.grpc.Metadata;
21-
import com.couchbase.client.core.deps.io.grpc.Status;
2219
import com.couchbase.client.core.deps.io.netty.channel.ChannelPipeline;
23-
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpHeaderNames;
24-
import com.couchbase.client.core.deps.io.netty.handler.codec.http.HttpRequest;
2520
import com.couchbase.client.core.endpoint.EndpointContext;
2621
import com.couchbase.client.core.io.netty.kv.SaslAuthenticationHandler;
2722
import com.couchbase.client.core.io.netty.kv.SaslListMechanismsHandler;
2823
import com.couchbase.client.core.io.netty.kv.sasl.SaslHelper;
29-
import com.couchbase.client.core.service.ServiceType;
3024
import reactor.util.annotation.Nullable;
3125

3226
import java.util.Base64;
3327
import java.util.EnumSet;
3428
import java.util.Set;
35-
import java.util.concurrent.Executor;
3629
import java.util.function.Supplier;
3730

38-
import static com.couchbase.client.core.deps.io.grpc.Metadata.ASCII_STRING_MARSHALLER;
3931
import static com.couchbase.client.core.io.netty.kv.sasl.SaslHelper.platformHasSaslPlain;
4032
import static com.couchbase.client.core.util.CbCollections.setCopyOf;
4133
import static com.couchbase.client.core.util.CbCollections.setOf;
@@ -142,8 +134,8 @@ private PasswordAuthenticator(final Builder builder) {
142134
cachedHttpAuthHeader = builder.dynamicCredentials ? null : encodeAuthHttpHeader(this.usernameAndPassword.get());
143135
}
144136

145-
// Visible for testing
146-
String getAuthHeaderValue() {
137+
@Override
138+
public String getAuthHeaderValue() {
147139
return cachedHttpAuthHeader != null
148140
? cachedHttpAuthHeader
149141
: encodeAuthHttpHeader(this.usernameAndPassword.get());
@@ -170,36 +162,6 @@ public void authKeyValueConnection(final EndpointContext ctx, final ChannelPipel
170162
));
171163
}
172164

173-
@Override
174-
public void authHttpRequest(final ServiceType serviceType, final HttpRequest request) {
175-
request.headers().add(
176-
HttpHeaderNames.AUTHORIZATION,
177-
getAuthHeaderValue()
178-
);
179-
}
180-
181-
@Override
182-
public CallCredentials protostellarCallCredentials() {
183-
return new CallCredentials() {
184-
@Override
185-
public void applyRequestMetadata(RequestInfo requestInfo, Executor executor, MetadataApplier applier) {
186-
executor.execute(() -> {
187-
try {
188-
Metadata headers = new Metadata();
189-
headers.put(Metadata.Key.of("Authorization", ASCII_STRING_MARSHALLER), getAuthHeaderValue());
190-
applier.apply(headers);
191-
} catch (Throwable e) {
192-
applier.fail(Status.UNAUTHENTICATED.withCause(e));
193-
}
194-
});
195-
}
196-
197-
@Override
198-
public void thisUsesUnstableApi() {
199-
}
200-
};
201-
}
202-
203165
@Override
204166
public boolean requiresTls() {
205167
return false;

0 commit comments

Comments
 (0)