Revert "core/auth: Remove CallCredentials2 (#8464)"

This reverts commit 7cde473efa.
This commit is contained in:
Sergii Tkachenko 2021-09-02 20:20:20 -04:00 committed by GitHub
parent 62fafe7eda
commit a91cc85dfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 428 additions and 47 deletions

View File

@ -0,0 +1,73 @@
/*
* Copyright 2016 The gRPC Authors
*
* Licensed 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 io.grpc;
import java.util.concurrent.Executor;
/**
* The new interface for {@link CallCredentials}.
*
* <p>THIS CLASS NAME IS TEMPORARY and is part of a migration. This class will BE DELETED as it
* replaces {@link CallCredentials} in short-term. THIS CLASS IS ONLY REFERENCED BY IMPLEMENTIONS.
* All consumers should be always referencing {@link CallCredentials}.
*
* @deprecated the new interface has been promoted into {@link CallCredentials}. Implementations
* should switch back to "{@code extends CallCredentials}".
*/
@Deprecated
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4901")
public abstract class CallCredentials2 extends CallCredentials {
/**
* Pass the credential data to the given {@link MetadataApplier}, which will propagate it to the
* request metadata.
*
* <p>It is called for each individual RPC, within the {@link Context} of the call, before the
* stream is about to be created on a transport. Implementations should not block in this
* method. If metadata is not immediately available, e.g., needs to be fetched from network, the
* implementation may give the {@code applier} to an asynchronous task which will eventually call
* the {@code applier}. The RPC proceeds only after the {@code applier} is called.
*
* @param requestInfo request-related information
* @param appExecutor The application thread-pool. It is provided to the implementation in case it
* needs to perform blocking operations.
* @param applier The outlet of the produced headers. It can be called either before or after this
* method returns.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1914")
public abstract void applyRequestMetadata(
RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier);
@Override
public final void applyRequestMetadata(
RequestInfo requestInfo, Executor appExecutor,
final CallCredentials.MetadataApplier applier) {
applyRequestMetadata(requestInfo, appExecutor, new MetadataApplier() {
@Override
public void apply(Metadata headers) {
applier.apply(headers);
}
@Override
public void fail(Status status) {
applier.fail(status);
}
});
}
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1914")
public abstract static class MetadataApplier extends CallCredentials.MetadataApplier {}
}

View File

@ -42,9 +42,11 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Wraps {@link Credentials} as a {@link io.grpc.CallCredentials}.
* Wraps {@link Credentials} as a {@link CallCredentials}.
*/
final class GoogleAuthLibraryCallCredentials extends io.grpc.CallCredentials {
// TODO(zhangkun83): remove the suppression after we change the base class to CallCredential
@SuppressWarnings("deprecation")
final class GoogleAuthLibraryCallCredentials extends io.grpc.CallCredentials2 {
private static final Logger log
= Logger.getLogger(GoogleAuthLibraryCallCredentials.class.getName());
private static final JwtHelper jwtHelper

View File

@ -0,0 +1,351 @@
/*
* Copyright 2016 The gRPC Authors
*
* Licensed 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 io.grpc.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import io.grpc.Attributes;
import io.grpc.CallCredentials.MetadataApplier;
import io.grpc.CallCredentials.RequestInfo;
import io.grpc.CallOptions;
import io.grpc.ChannelLogger;
import io.grpc.ClientStreamTracer;
import io.grpc.IntegerMarshaller;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.SecurityLevel;
import io.grpc.Status;
import io.grpc.StringMarshaller;
import java.net.SocketAddress;
import java.util.concurrent.Executor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
/**
* Unit test for {@link CallCredentials2} applying functionality implemented by {@link
* CallCredentialsApplyingTransportFactory} and {@link MetadataApplierImpl}.
*/
@SuppressWarnings("deprecation")
@RunWith(JUnit4.class)
public class CallCredentials2ApplyingTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
@Mock
private ClientTransportFactory mockTransportFactory;
@Mock
private ConnectionClientTransport mockTransport;
@Mock
private ClientStream mockStream;
@Mock
private io.grpc.CallCredentials2 mockCreds;
@Mock
private Executor mockExecutor;
@Mock
private SocketAddress address;
// Noop logger;
@Mock
private ChannelLogger channelLogger;
private static final String AUTHORITY = "testauthority";
private static final String USER_AGENT = "testuseragent";
private static final Attributes.Key<String> ATTR_KEY = Attributes.Key.create("somekey");
private static final String ATTR_VALUE = "somevalue";
private static final MethodDescriptor<String, Integer> method =
MethodDescriptor.<String, Integer>newBuilder()
.setType(MethodDescriptor.MethodType.UNKNOWN)
.setFullMethodName("service/method")
.setRequestMarshaller(new StringMarshaller())
.setResponseMarshaller(new IntegerMarshaller())
.build();
private static final Metadata.Key<String> ORIG_HEADER_KEY =
Metadata.Key.of("header1", Metadata.ASCII_STRING_MARSHALLER);
private static final String ORIG_HEADER_VALUE = "some original header value";
private static final Metadata.Key<String> CREDS_KEY =
Metadata.Key.of("test-creds", Metadata.ASCII_STRING_MARSHALLER);
private static final String CREDS_VALUE = "some credentials";
private static final ClientStreamTracer[] tracers = new ClientStreamTracer[] {
new ClientStreamTracer() {}
};
private final Metadata origHeaders = new Metadata();
private ForwardingConnectionClientTransport transport;
private CallOptions callOptions;
@Before
public void setUp() {
ClientTransportFactory.ClientTransportOptions clientTransportOptions =
new ClientTransportFactory.ClientTransportOptions()
.setAuthority(AUTHORITY)
.setUserAgent(USER_AGENT);
origHeaders.put(ORIG_HEADER_KEY, ORIG_HEADER_VALUE);
when(mockTransportFactory.newClientTransport(address, clientTransportOptions, channelLogger))
.thenReturn(mockTransport);
when(mockTransport.newStream(
same(method), any(Metadata.class), any(CallOptions.class),
ArgumentMatchers.<ClientStreamTracer[]>any()))
.thenReturn(mockStream);
ClientTransportFactory transportFactory = new CallCredentialsApplyingTransportFactory(
mockTransportFactory, null, mockExecutor);
transport = (ForwardingConnectionClientTransport)
transportFactory.newClientTransport(address, clientTransportOptions, channelLogger);
callOptions = CallOptions.DEFAULT.withCallCredentials(mockCreds);
verify(mockTransportFactory).newClientTransport(address, clientTransportOptions, channelLogger);
assertSame(mockTransport, transport.delegate());
}
@Test
public void parameterPropagation_base() {
Attributes transportAttrs = Attributes.newBuilder().set(ATTR_KEY, ATTR_VALUE).build();
when(mockTransport.getAttributes()).thenReturn(transportAttrs);
transport.newStream(method, origHeaders, callOptions, tracers);
ArgumentCaptor<RequestInfo> infoCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
infoCaptor.capture(), same(mockExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
RequestInfo info = infoCaptor.getValue();
assertSame(method, info.getMethodDescriptor());
assertSame(ATTR_VALUE, info.getTransportAttrs().get(ATTR_KEY));
assertSame(AUTHORITY, info.getAuthority());
assertSame(SecurityLevel.NONE, info.getSecurityLevel());
}
@Test
public void parameterPropagation_transportSetSecurityLevel() {
Attributes transportAttrs = Attributes.newBuilder()
.set(ATTR_KEY, ATTR_VALUE)
.set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.INTEGRITY)
.build();
when(mockTransport.getAttributes()).thenReturn(transportAttrs);
transport.newStream(method, origHeaders, callOptions, tracers);
ArgumentCaptor<RequestInfo> infoCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
infoCaptor.capture(), same(mockExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
RequestInfo info = infoCaptor.getValue();
assertSame(method, info.getMethodDescriptor());
assertSame(ATTR_VALUE, info.getTransportAttrs().get(ATTR_KEY));
assertSame(AUTHORITY, info.getAuthority());
assertSame(SecurityLevel.INTEGRITY, info.getSecurityLevel());
}
@Test
public void parameterPropagation_callOptionsSetAuthority() {
Attributes transportAttrs = Attributes.newBuilder()
.set(ATTR_KEY, ATTR_VALUE)
.build();
when(mockTransport.getAttributes()).thenReturn(transportAttrs);
Executor anotherExecutor = mock(Executor.class);
transport.newStream(
method, origHeaders,
callOptions.withAuthority("calloptions-authority").withExecutor(anotherExecutor),
tracers);
ArgumentCaptor<RequestInfo> infoCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
infoCaptor.capture(), same(anotherExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
RequestInfo info = infoCaptor.getValue();
assertSame(method, info.getMethodDescriptor());
assertSame(ATTR_VALUE, info.getTransportAttrs().get(ATTR_KEY));
assertEquals("calloptions-authority", info.getAuthority());
assertSame(SecurityLevel.NONE, info.getSecurityLevel());
}
@Test
public void credentialThrows() {
final RuntimeException ex = new RuntimeException();
when(mockTransport.getAttributes()).thenReturn(Attributes.EMPTY);
doThrow(ex).when(mockCreds).applyRequestMetadata(
any(RequestInfo.class), same(mockExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
FailingClientStream stream =
(FailingClientStream) transport.newStream(method, origHeaders, callOptions, tracers);
verify(mockTransport, never()).newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class),
ArgumentMatchers.<ClientStreamTracer[]>any());
assertEquals(Status.Code.UNAUTHENTICATED, stream.getError().getCode());
assertSame(ex, stream.getError().getCause());
transport.shutdown(Status.UNAVAILABLE);
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdown(Status.UNAVAILABLE);
}
@Test
public void applyMetadata_inline() {
when(mockTransport.getAttributes()).thenReturn(Attributes.EMPTY);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
MetadataApplier applier = (MetadataApplier) invocation.getArguments()[2];
Metadata headers = new Metadata();
headers.put(CREDS_KEY, CREDS_VALUE);
applier.apply(headers);
return null;
}
}).when(mockCreds).applyRequestMetadata(
any(RequestInfo.class), same(mockExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
ClientStream stream = transport.newStream(method, origHeaders, callOptions, tracers);
verify(mockTransport).newStream(method, origHeaders, callOptions, tracers);
assertSame(mockStream, stream);
assertEquals(CREDS_VALUE, origHeaders.get(CREDS_KEY));
assertEquals(ORIG_HEADER_VALUE, origHeaders.get(ORIG_HEADER_KEY));
transport.shutdown(Status.UNAVAILABLE);
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdown(Status.UNAVAILABLE);
}
@Test
public void fail_inline() {
final Status error = Status.FAILED_PRECONDITION.withDescription("channel not secure for creds");
when(mockTransport.getAttributes()).thenReturn(Attributes.EMPTY);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
MetadataApplier applier = (MetadataApplier) invocation.getArguments()[2];
applier.fail(error);
return null;
}
}).when(mockCreds).applyRequestMetadata(
any(RequestInfo.class), same(mockExecutor),
any(io.grpc.CallCredentials2.MetadataApplier.class));
FailingClientStream stream =
(FailingClientStream) transport.newStream(method, origHeaders, callOptions, tracers);
verify(mockTransport, never()).newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class),
ArgumentMatchers.<ClientStreamTracer[]>any());
assertSame(error, stream.getError());
transport.shutdownNow(Status.UNAVAILABLE);
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdownNow(Status.UNAVAILABLE);
}
@Test
public void applyMetadata_delayed() {
when(mockTransport.getAttributes()).thenReturn(Attributes.EMPTY);
// Will call applyRequestMetadata(), which is no-op.
DelayedStream stream = (DelayedStream) transport.newStream(
method, origHeaders, callOptions, tracers);
ArgumentCaptor<MetadataApplier> applierCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
any(RequestInfo.class), same(mockExecutor), applierCaptor.capture());
verify(mockTransport, never()).newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class),
ArgumentMatchers.<ClientStreamTracer[]>any());
transport.shutdown(Status.UNAVAILABLE);
verify(mockTransport, never()).shutdown(Status.UNAVAILABLE);
Metadata headers = new Metadata();
headers.put(CREDS_KEY, CREDS_VALUE);
applierCaptor.getValue().apply(headers);
verify(mockTransport).newStream(method, origHeaders, callOptions, tracers);
assertSame(mockStream, stream.getRealStream());
assertEquals(CREDS_VALUE, origHeaders.get(CREDS_KEY));
assertEquals(ORIG_HEADER_VALUE, origHeaders.get(ORIG_HEADER_KEY));
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdown(Status.UNAVAILABLE);
}
@Test
public void fail_delayed() {
when(mockTransport.getAttributes()).thenReturn(Attributes.EMPTY);
// Will call applyRequestMetadata(), which is no-op.
DelayedStream stream = (DelayedStream) transport.newStream(
method, origHeaders, callOptions, tracers);
ArgumentCaptor<MetadataApplier> applierCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
any(RequestInfo.class), same(mockExecutor), applierCaptor.capture());
Status error = Status.FAILED_PRECONDITION.withDescription("channel not secure for creds");
applierCaptor.getValue().fail(error);
verify(mockTransport, never()).newStream(
any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class),
ArgumentMatchers.<ClientStreamTracer[]>any());
FailingClientStream failingStream = (FailingClientStream) stream.getRealStream();
assertSame(error, failingStream.getError());
transport.shutdown(Status.UNAVAILABLE);
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdown(Status.UNAVAILABLE);
}
@Test
public void noCreds() {
callOptions = callOptions.withCallCredentials(null);
ClientStream stream = transport.newStream(method, origHeaders, callOptions, tracers);
verify(mockTransport).newStream(method, origHeaders, callOptions, tracers);
assertSame(mockStream, stream);
assertNull(origHeaders.get(CREDS_KEY));
assertEquals(ORIG_HEADER_VALUE, origHeaders.get(ORIG_HEADER_KEY));
transport.shutdown(Status.UNAVAILABLE);
assertTrue(transport.newStream(method, origHeaders, callOptions, tracers)
instanceof FailingClientStream);
verify(mockTransport).shutdown(Status.UNAVAILABLE);
}
}

View File

@ -176,51 +176,6 @@ public class CallCredentialsApplyingTest {
assertSame(SecurityLevel.INTEGRITY, info.getSecurityLevel());
}
@Test
public void parameterPropagation_transportSetSecurityLevel() {
Attributes transportAttrs = Attributes.newBuilder()
.set(ATTR_KEY, ATTR_VALUE)
.set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.INTEGRITY)
.build();
when(mockTransport.getAttributes()).thenReturn(transportAttrs);
transport.newStream(method, origHeaders, callOptions, tracers);
ArgumentCaptor<RequestInfo> infoCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
infoCaptor.capture(), same(mockExecutor),
any(io.grpc.CallCredentials.MetadataApplier.class));
RequestInfo info = infoCaptor.getValue();
assertSame(method, info.getMethodDescriptor());
assertSame(ATTR_VALUE, info.getTransportAttrs().get(ATTR_KEY));
assertSame(AUTHORITY, info.getAuthority());
assertSame(SecurityLevel.INTEGRITY, info.getSecurityLevel());
}
@Test
public void parameterPropagation_callOptionsSetAuthority() {
Attributes transportAttrs = Attributes.newBuilder()
.set(ATTR_KEY, ATTR_VALUE)
.build();
when(mockTransport.getAttributes()).thenReturn(transportAttrs);
Executor anotherExecutor = mock(Executor.class);
transport.newStream(
method, origHeaders,
callOptions.withAuthority("calloptions-authority").withExecutor(anotherExecutor),
tracers);
ArgumentCaptor<RequestInfo> infoCaptor = ArgumentCaptor.forClass(null);
verify(mockCreds).applyRequestMetadata(
infoCaptor.capture(), same(anotherExecutor),
any(io.grpc.CallCredentials.MetadataApplier.class));
RequestInfo info = infoCaptor.getValue();
assertSame(method, info.getMethodDescriptor());
assertSame(ATTR_VALUE, info.getTransportAttrs().get(ATTR_KEY));
assertEquals("calloptions-authority", info.getAuthority());
assertSame(SecurityLevel.NONE, info.getSecurityLevel());
}
@Test
public void credentialThrows() {
final RuntimeException ex = new RuntimeException();