mirror of
https://github.com/google/nomulus
synced 2026-02-08 05:50:24 +00:00
Refactor OIDC-based auth mechanism (#2049)
This PR changes the two flavors of OIDC authentication mechanisms to verify the same audience. This allows the same token to pass both mechanisms. Previously the regular OIDC flavor uses the project id as its required audience, which does not work for local user credentials (such as ones used by the nomulus tool), which requires a valid OAuth client ID as audience when minting the token (project id is NOT a valid OAuth client ID). I considered allowing multiple audiences, but the result is not as clean as just using the same everywhere, because the fall-through logic would have generated a lot of noises for failed attempts. This PR also changes the client side to solely use OIDC token whenever possible, including the proxy, cloud scheduler and cloud tasks. The nomulus tool still uses OAuth access token by default because it requires USER level authentication, which in turn requires us to fill the User table with objects corresponding to the email address of everyone needing access to the tool. TESTED=verified each client is able to make authenticated calls on QA with or without IAP.
This commit is contained in:
@@ -24,7 +24,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.util.SelfSignedCaCertificate;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -90,7 +89,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
}
|
||||
|
||||
/** Get a {@link ByteBuf} that represents the raw epp request with the given content. */
|
||||
private ByteBuf getByteBufFromContent(byte[] content) {
|
||||
private static ByteBuf getByteBufFromContent(byte[] content) {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
buffer.writeInt(content.length + HEADER_LENGTH);
|
||||
buffer.writeBytes(content);
|
||||
@@ -102,18 +101,17 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
new String(content, UTF_8),
|
||||
PROXY_CONFIG.epp.relayHost,
|
||||
PROXY_CONFIG.epp.relayPath,
|
||||
TestModule.provideFakeCredentials().get(),
|
||||
TestModule.provideFakeIdToken().get(),
|
||||
getCertificateHash(certificate),
|
||||
CLIENT_ADDRESS,
|
||||
TestModule.provideIapClientId(),
|
||||
cookies);
|
||||
}
|
||||
|
||||
private FullHttpResponse makeEppHttpResponse(byte[] content, Cookie... cookies) {
|
||||
private static FullHttpResponse makeEppHttpResponse(byte[] content, Cookie... cookies) {
|
||||
return makeEppHttpResponse(content, HttpResponseStatus.OK, cookies);
|
||||
}
|
||||
|
||||
private FullHttpResponse makeEppHttpResponse(
|
||||
private static FullHttpResponse makeEppHttpResponse(
|
||||
byte[] content, HttpResponseStatus status, Cookie... cookies) {
|
||||
return TestUtils.makeEppHttpResponse(new String(content, UTF_8), status, cookies);
|
||||
}
|
||||
@@ -121,7 +119,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
@BeforeEach
|
||||
@Override
|
||||
void beforeEach() throws Exception {
|
||||
testComponent = makeTestComponent(new FakeClock());
|
||||
testComponent = makeTestComponent();
|
||||
certificate = SelfSignedCaCertificate.create().cert();
|
||||
initializeChannel(
|
||||
ch -> {
|
||||
@@ -129,6 +127,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||
ch.attr(CLIENT_CERTIFICATE_PROMISE_KEY).set(ch.eventLoop().newPromise());
|
||||
addAllTestableHandlers(ch);
|
||||
});
|
||||
@SuppressWarnings("unused")
|
||||
Promise<X509Certificate> unusedPromise =
|
||||
channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().setSuccess(certificate);
|
||||
}
|
||||
|
||||
@@ -17,14 +17,7 @@ package google.registry.proxy;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.proxy.ProxyConfig.Environment.LOCAL;
|
||||
import static google.registry.proxy.ProxyConfig.getProxyConfig;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.auth.oauth2.AccessToken;
|
||||
import com.google.auth.oauth2.ComputeEngineCredentials;
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.auth.oauth2.IdToken;
|
||||
import com.google.auth.oauth2.IdTokenProvider.Option;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -59,9 +52,7 @@ import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -80,14 +71,14 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
* correctly performed by various handlers attached to its pipeline. Non-business essential handlers
|
||||
* should be excluded.
|
||||
*
|
||||
* <p>Subclass should implement an no-arg constructor that calls constructors of this class,
|
||||
* <p>Subclass should implement a no-arg constructor that calls constructors of this class,
|
||||
* providing the method reference of the {@link TestComponent} method to call to obtain the list of
|
||||
* {@link ChannelHandler} providers for the {@link Protocol} to test, and optionally a set of {@link
|
||||
* ChannelHandler} classes to exclude from testing.
|
||||
*/
|
||||
public abstract class ProtocolModuleTest {
|
||||
|
||||
static final ProxyConfig PROXY_CONFIG = getProxyConfig(Environment.LOCAL);
|
||||
static final ProxyConfig PROXY_CONFIG = getProxyConfig(LOCAL);
|
||||
|
||||
TestComponent testComponent;
|
||||
|
||||
@@ -112,7 +103,7 @@ public abstract class ProtocolModuleTest {
|
||||
FullHttpResponseRelayHandler.class,
|
||||
// This handler is tested in its own unit tests. It is installed in web whois redirect
|
||||
// protocols. The end-to-end tests for the rest of the handlers in its pipeline need to
|
||||
// be able to emit incoming requests out of the channel for assertions. Therefore this
|
||||
// be able to emit incoming requests out of the channel for assertions. Therefore, this
|
||||
// handler is removed from the pipeline.
|
||||
WebWhoisRedirectHandler.class,
|
||||
// The rest are not part of business logic and do not need to be tested, obviously.
|
||||
@@ -150,7 +141,7 @@ public abstract class ProtocolModuleTest {
|
||||
this(handlerProvidersMethod, DEFAULT_EXCLUDED_HANDLERS);
|
||||
}
|
||||
|
||||
/** Excludes handler providers that are not of interested for testing. */
|
||||
/** Excludes handler providers that are not of interest for testing. */
|
||||
private ImmutableList<Provider<? extends ChannelHandler>> excludeHandlerProvidersForTesting(
|
||||
ImmutableList<Provider<? extends ChannelHandler>> handlerProviders) {
|
||||
return handlerProviders.stream()
|
||||
@@ -177,7 +168,7 @@ public abstract class ProtocolModuleTest {
|
||||
}
|
||||
}
|
||||
|
||||
static TestComponent makeTestComponent(FakeClock fakeClock) {
|
||||
static TestComponent makeTestComponent() {
|
||||
return DaggerProtocolModuleTest_TestComponent.builder()
|
||||
.testModule(new TestModule(new FakeClock()))
|
||||
.build();
|
||||
@@ -185,7 +176,7 @@ public abstract class ProtocolModuleTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
testComponent = makeTestComponent(new FakeClock());
|
||||
testComponent = makeTestComponent();
|
||||
initializeChannel(this::addAllTestableHandlers);
|
||||
}
|
||||
|
||||
@@ -244,10 +235,11 @@ public abstract class ProtocolModuleTest {
|
||||
this.fakeClock = fakeClock;
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@Named("iapClientId")
|
||||
public static Optional<String> provideIapClientId() {
|
||||
return Optional.of("iapClientId");
|
||||
@Named("clientId")
|
||||
public static String provideClientId() {
|
||||
return "clientId";
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@@ -264,19 +256,9 @@ public abstract class ProtocolModuleTest {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
static Supplier<GoogleCredentials> provideFakeCredentials() {
|
||||
ComputeEngineCredentials mockCredentials = mock(ComputeEngineCredentials.class);
|
||||
when(mockCredentials.getAccessToken()).thenReturn(new AccessToken("fake.test.token", null));
|
||||
IdToken mockIdToken = mock(IdToken.class);
|
||||
when(mockIdToken.getTokenValue()).thenReturn("fake.test.id.token");
|
||||
try {
|
||||
when(mockCredentials.idTokenWithAudience(
|
||||
"iapClientId", ImmutableList.of(Option.FORMAT_FULL)))
|
||||
.thenReturn(mockIdToken);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return Suppliers.ofInstance(mockCredentials);
|
||||
@Named("idToken")
|
||||
static Supplier<String> provideFakeIdToken() {
|
||||
return Suppliers.ofInstance("fake.test.id.token");
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@@ -306,7 +288,7 @@ public abstract class ProtocolModuleTest {
|
||||
@Singleton
|
||||
@Provides
|
||||
static Environment provideEnvironment() {
|
||||
return Environment.LOCAL;
|
||||
return LOCAL;
|
||||
}
|
||||
|
||||
@Singleton
|
||||
|
||||
@@ -17,10 +17,6 @@ package google.registry.proxy;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.auth.oauth2.IdTokenProvider;
|
||||
import com.google.auth.oauth2.IdTokenProvider.Option;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.util.ProxyHttpHeaders;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
@@ -39,10 +35,11 @@ import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
|
||||
import io.netty.handler.codec.http.cookie.Cookie;
|
||||
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Utility class for various helper methods used in testing. */
|
||||
public class TestUtils {
|
||||
public final class TestUtils {
|
||||
|
||||
private TestUtils() {}
|
||||
|
||||
public static FullHttpRequest makeHttpPostRequest(String content, String host, String path) {
|
||||
ByteBuf buf = Unpooled.wrappedBuffer(content.getBytes(US_ASCII));
|
||||
@@ -77,19 +74,13 @@ public class TestUtils {
|
||||
}
|
||||
|
||||
public static FullHttpRequest makeWhoisHttpRequest(
|
||||
String content,
|
||||
String host,
|
||||
String path,
|
||||
GoogleCredentials credentials,
|
||||
Optional<String> iapClientId)
|
||||
throws IOException {
|
||||
String content, String host, String path, String idToken) throws IOException {
|
||||
FullHttpRequest request = makeHttpPostRequest(content, host, path);
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", "Bearer " + credentials.getAccessToken().getTokenValue())
|
||||
.set("authorization", "Bearer " + idToken)
|
||||
.set(HttpHeaderNames.CONTENT_TYPE, "text/plain")
|
||||
.set("accept", "text/plain");
|
||||
maybeSetProxyAuthForIap(request, credentials, iapClientId);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -97,21 +88,19 @@ public class TestUtils {
|
||||
String content,
|
||||
String host,
|
||||
String path,
|
||||
GoogleCredentials credentials,
|
||||
String idToken,
|
||||
String sslClientCertificateHash,
|
||||
String clientAddress,
|
||||
Optional<String> iapClientId,
|
||||
Cookie... cookies)
|
||||
throws IOException {
|
||||
FullHttpRequest request = makeHttpPostRequest(content, host, path);
|
||||
request
|
||||
.headers()
|
||||
.set("authorization", "Bearer " + credentials.getAccessToken().getTokenValue())
|
||||
.set("authorization", "Bearer " + idToken)
|
||||
.set(HttpHeaderNames.CONTENT_TYPE, "application/epp+xml")
|
||||
.set("accept", "application/epp+xml")
|
||||
.set(ProxyHttpHeaders.CERTIFICATE_HASH, sslClientCertificateHash)
|
||||
.set(ProxyHttpHeaders.IP_ADDRESS, clientAddress);
|
||||
maybeSetProxyAuthForIap(request, credentials, iapClientId);
|
||||
if (cookies.length != 0) {
|
||||
request.headers().set("cookie", ClientCookieEncoder.STRICT.encode(cookies));
|
||||
}
|
||||
@@ -140,7 +129,7 @@ public class TestUtils {
|
||||
* <p>This method is needed because an HTTP message decoded and aggregated from inbound {@link
|
||||
* ByteBuf} is of a different class than the one written to the outbound {@link ByteBuf}, and The
|
||||
* {@link ByteBuf} implementations that hold the content of the HTTP messages are different, even
|
||||
* though the actual content, headers, etc are the same.
|
||||
* though the actual content, headers, etc. are the same.
|
||||
*
|
||||
* <p>This method is not type-safe, msg1 & msg2 can be a request and a response, respectively. Do
|
||||
* not use this method directly.
|
||||
@@ -161,16 +150,4 @@ public class TestUtils {
|
||||
public static void assertHttpRequestEquivalent(HttpRequest req1, HttpRequest req2) {
|
||||
assertHttpMessageEquivalent(req1, req2);
|
||||
}
|
||||
|
||||
private static void maybeSetProxyAuthForIap(
|
||||
FullHttpRequest request, GoogleCredentials credentials, Optional<String> iapClientId)
|
||||
throws IOException {
|
||||
if (iapClientId.isPresent()) {
|
||||
String idTokenValue =
|
||||
((IdTokenProvider) credentials)
|
||||
.idTokenWithAudience(iapClientId.get(), ImmutableList.of(Option.FORMAT_FULL))
|
||||
.getTokenValue();
|
||||
request.headers().set("proxy-authorization", "Bearer " + idTokenValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ class WhoisProtocolModuleTest extends ProtocolModuleTest {
|
||||
"test.tld",
|
||||
PROXY_CONFIG.whois.relayHost,
|
||||
PROXY_CONFIG.whois.relayPath,
|
||||
TestModule.provideFakeCredentials().get(),
|
||||
TestModule.provideIapClientId());
|
||||
TestModule.provideFakeIdToken().get());
|
||||
assertThat(actualRequest).isEqualTo(expectedRequest);
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
// Nothing more to read.
|
||||
@@ -89,8 +88,7 @@ class WhoisProtocolModuleTest extends ProtocolModuleTest {
|
||||
"test1.tld",
|
||||
PROXY_CONFIG.whois.relayHost,
|
||||
PROXY_CONFIG.whois.relayPath,
|
||||
TestModule.provideFakeCredentials().get(),
|
||||
TestModule.provideIapClientId());
|
||||
TestModule.provideFakeIdToken().get());
|
||||
assertThat(actualRequest1).isEqualTo(expectedRequest1);
|
||||
// No more message at this point.
|
||||
assertThat((Object) channel.readInbound()).isNull();
|
||||
@@ -104,8 +102,7 @@ class WhoisProtocolModuleTest extends ProtocolModuleTest {
|
||||
"test2.tld",
|
||||
PROXY_CONFIG.whois.relayHost,
|
||||
PROXY_CONFIG.whois.relayPath,
|
||||
TestModule.provideFakeCredentials().get(),
|
||||
TestModule.provideIapClientId());
|
||||
TestModule.provideFakeIdToken().get());
|
||||
assertThat(actualRequest2).isEqualTo(expectedRequest2);
|
||||
// The third message is not complete yet.
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
|
||||
@@ -25,14 +25,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.auth.oauth2.AccessToken;
|
||||
import com.google.auth.oauth2.ComputeEngineCredentials;
|
||||
import com.google.auth.oauth2.IdToken;
|
||||
import com.google.auth.oauth2.IdTokenProvider.Option;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.proxy.TestUtils;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.proxy.metric.FrontendMetrics;
|
||||
@@ -52,7 +46,6 @@ import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||
import io.netty.util.concurrent.Promise;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -69,27 +62,18 @@ class EppServiceHandlerTest {
|
||||
private static final String RELAY_PATH = "/epp";
|
||||
private static final String CLIENT_ADDRESS = "epp.client.tld";
|
||||
private static final String PROTOCOL = "epp";
|
||||
private static final String IAP_CLIENT_ID = "iapClientId";
|
||||
|
||||
private static final ComputeEngineCredentials mockCredentials =
|
||||
mock(ComputeEngineCredentials.class);
|
||||
private static final String ID_TOKEN = "fake.id.token";
|
||||
|
||||
private X509Certificate clientCertificate;
|
||||
|
||||
private final FrontendMetrics metrics = mock(FrontendMetrics.class);
|
||||
|
||||
private final EppServiceHandler eppServiceHandler =
|
||||
new EppServiceHandler(
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
() -> mockCredentials,
|
||||
Optional.of(IAP_CLIENT_ID),
|
||||
HELLO.getBytes(UTF_8),
|
||||
metrics);
|
||||
new EppServiceHandler(RELAY_HOST, RELAY_PATH, () -> ID_TOKEN, HELLO.getBytes(UTF_8), metrics);
|
||||
|
||||
private EmbeddedChannel channel;
|
||||
|
||||
private void setHandshakeSuccess(EmbeddedChannel channel, X509Certificate certificate) {
|
||||
private static void setHandshakeSuccess(EmbeddedChannel channel, X509Certificate certificate) {
|
||||
@SuppressWarnings("unused")
|
||||
Promise<X509Certificate> unusedPromise =
|
||||
channel.attr(CLIENT_CERTIFICATE_PROMISE_KEY).get().setSuccess(certificate);
|
||||
@@ -99,7 +83,8 @@ class EppServiceHandlerTest {
|
||||
setHandshakeSuccess(channel, clientCertificate);
|
||||
}
|
||||
|
||||
private void setHandshakeFailure(EmbeddedChannel channel) {
|
||||
private static void setHandshakeFailure(EmbeddedChannel channel) {
|
||||
@SuppressWarnings("unused")
|
||||
Promise<X509Certificate> unusedPromise =
|
||||
channel
|
||||
.attr(CLIENT_CERTIFICATE_PROMISE_KEY)
|
||||
@@ -116,25 +101,19 @@ class EppServiceHandlerTest {
|
||||
content,
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
mockCredentials,
|
||||
ID_TOKEN,
|
||||
getCertificateHash(clientCertificate),
|
||||
CLIENT_ADDRESS,
|
||||
Optional.of(IAP_CLIENT_ID),
|
||||
cookies);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
when(mockCredentials.getAccessToken()).thenReturn(new AccessToken("this.access.token", null));
|
||||
IdToken mockIdToken = mock(IdToken.class);
|
||||
when(mockIdToken.getTokenValue()).thenReturn("fake.test.id.token");
|
||||
when(mockCredentials.idTokenWithAudience(IAP_CLIENT_ID, ImmutableList.of(Option.FORMAT_FULL)))
|
||||
.thenReturn(mockIdToken);
|
||||
clientCertificate = SelfSignedCaCertificate.create().cert();
|
||||
channel = setUpNewChannel(eppServiceHandler);
|
||||
}
|
||||
|
||||
private EmbeddedChannel setUpNewChannel(EppServiceHandler handler) {
|
||||
private static EmbeddedChannel setUpNewChannel(EppServiceHandler handler) {
|
||||
return new EmbeddedChannel(
|
||||
DefaultChannelId.newInstance(),
|
||||
new ChannelInitializer<EmbeddedChannel>() {
|
||||
@@ -165,12 +144,7 @@ class EppServiceHandlerTest {
|
||||
// Set up the second channel.
|
||||
EppServiceHandler eppServiceHandler2 =
|
||||
new EppServiceHandler(
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
() -> mockCredentials,
|
||||
Optional.empty(),
|
||||
HELLO.getBytes(UTF_8),
|
||||
metrics);
|
||||
RELAY_HOST, RELAY_PATH, () -> ID_TOKEN, HELLO.getBytes(UTF_8), metrics);
|
||||
EmbeddedChannel channel2 = setUpNewChannel(eppServiceHandler2);
|
||||
setHandshakeSuccess(channel2, clientCertificate);
|
||||
|
||||
@@ -190,12 +164,7 @@ class EppServiceHandlerTest {
|
||||
// Set up the second channel.
|
||||
EppServiceHandler eppServiceHandler2 =
|
||||
new EppServiceHandler(
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
() -> mockCredentials,
|
||||
Optional.empty(),
|
||||
HELLO.getBytes(UTF_8),
|
||||
metrics);
|
||||
RELAY_HOST, RELAY_PATH, () -> ID_TOKEN, HELLO.getBytes(UTF_8), metrics);
|
||||
EmbeddedChannel channel2 = setUpNewChannel(eppServiceHandler2);
|
||||
X509Certificate clientCertificate2 = SelfSignedCaCertificate.create().cert();
|
||||
setHandshakeSuccess(channel2, clientCertificate2);
|
||||
@@ -358,38 +327,4 @@ class EppServiceHandlerTest {
|
||||
assertThat((Object) channel.readOutbound()).isNull();
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_withoutIapClientId() throws Exception {
|
||||
// Without an IAP client ID configured, we shouldn't include the proxy-authorization header
|
||||
EppServiceHandler nonIapServiceHandler =
|
||||
new EppServiceHandler(
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
() -> mockCredentials,
|
||||
Optional.empty(),
|
||||
HELLO.getBytes(UTF_8),
|
||||
metrics);
|
||||
channel = setUpNewChannel(nonIapServiceHandler);
|
||||
|
||||
setHandshakeSuccess();
|
||||
// First inbound message is hello.
|
||||
channel.readInbound();
|
||||
String content = "<epp>stuff</epp>";
|
||||
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
|
||||
FullHttpRequest request = channel.readInbound();
|
||||
assertThat(request)
|
||||
.isEqualTo(
|
||||
TestUtils.makeEppHttpRequest(
|
||||
content,
|
||||
RELAY_HOST,
|
||||
RELAY_PATH,
|
||||
mockCredentials,
|
||||
getCertificateHash(clientCertificate),
|
||||
CLIENT_ADDRESS,
|
||||
Optional.empty()));
|
||||
// Nothing further to pass to the next handler.
|
||||
assertThat((Object) channel.readInbound()).isNull();
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,14 +22,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.auth.oauth2.AccessToken;
|
||||
import com.google.auth.oauth2.ComputeEngineCredentials;
|
||||
import com.google.auth.oauth2.IdToken;
|
||||
import com.google.auth.oauth2.IdTokenProvider.Option;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||
import google.registry.proxy.metric.FrontendMetrics;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
@@ -40,7 +34,6 @@ import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -52,24 +45,16 @@ class WhoisServiceHandlerTest {
|
||||
private static final String QUERY_CONTENT = "test.tld";
|
||||
private static final String PROTOCOL = "whois";
|
||||
private static final String CLIENT_HASH = "none";
|
||||
private static final String IAP_CLIENT_ID = "iapClientId";
|
||||
private static final String ID_TOKEN = "fake.id.token";
|
||||
|
||||
private static final ComputeEngineCredentials mockCredentials =
|
||||
mock(ComputeEngineCredentials.class);
|
||||
private final FrontendMetrics metrics = mock(FrontendMetrics.class);
|
||||
|
||||
private final WhoisServiceHandler whoisServiceHandler =
|
||||
new WhoisServiceHandler(
|
||||
RELAY_HOST, RELAY_PATH, () -> mockCredentials, Optional.of(IAP_CLIENT_ID), metrics);
|
||||
new WhoisServiceHandler(RELAY_HOST, RELAY_PATH, () -> ID_TOKEN, metrics);
|
||||
private EmbeddedChannel channel;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
when(mockCredentials.getAccessToken()).thenReturn(new AccessToken("this.access.token", null));
|
||||
IdToken mockIdToken = mock(IdToken.class);
|
||||
when(mockIdToken.getTokenValue()).thenReturn("fake.test.id.token");
|
||||
when(mockCredentials.idTokenWithAudience(IAP_CLIENT_ID, ImmutableList.of(Option.FORMAT_FULL)))
|
||||
.thenReturn(mockIdToken);
|
||||
// Need to reset metrics for each test method, since they are static fields on the class and
|
||||
// shared between each run.
|
||||
channel = new EmbeddedChannel(whoisServiceHandler);
|
||||
@@ -89,8 +74,7 @@ class WhoisServiceHandlerTest {
|
||||
|
||||
// Setup second channel.
|
||||
WhoisServiceHandler whoisServiceHandler2 =
|
||||
new WhoisServiceHandler(
|
||||
RELAY_HOST, RELAY_PATH, () -> mockCredentials, Optional.empty(), metrics);
|
||||
new WhoisServiceHandler(RELAY_HOST, RELAY_PATH, () -> ID_TOKEN, metrics);
|
||||
EmbeddedChannel channel2 =
|
||||
// We need a new channel id so that it has a different hash code.
|
||||
// This only is needed for EmbeddedChannel because it has a dummy hash code implementation.
|
||||
@@ -104,8 +88,7 @@ class WhoisServiceHandlerTest {
|
||||
void testSuccess_fireInboundHttpRequest() throws Exception {
|
||||
ByteBuf inputBuffer = Unpooled.wrappedBuffer(QUERY_CONTENT.getBytes(US_ASCII));
|
||||
FullHttpRequest expectedRequest =
|
||||
makeWhoisHttpRequest(
|
||||
QUERY_CONTENT, RELAY_HOST, RELAY_PATH, mockCredentials, Optional.of(IAP_CLIENT_ID));
|
||||
makeWhoisHttpRequest(QUERY_CONTENT, RELAY_HOST, RELAY_PATH, ID_TOKEN);
|
||||
// Input data passed to next handler
|
||||
assertThat(channel.writeInbound(inputBuffer)).isTrue();
|
||||
FullHttpRequest inputRequest = channel.readInbound();
|
||||
@@ -128,27 +111,6 @@ class WhoisServiceHandlerTest {
|
||||
assertThat(channel.isActive()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_withoutIapClientId() throws Exception {
|
||||
// Without an IAP client ID configured, we shouldn't include the proxy-authorization header
|
||||
WhoisServiceHandler nonIapHandler =
|
||||
new WhoisServiceHandler(
|
||||
RELAY_HOST, RELAY_PATH, () -> mockCredentials, Optional.empty(), metrics);
|
||||
channel = new EmbeddedChannel(nonIapHandler);
|
||||
|
||||
ByteBuf inputBuffer = Unpooled.wrappedBuffer(QUERY_CONTENT.getBytes(US_ASCII));
|
||||
FullHttpRequest expectedRequest =
|
||||
makeWhoisHttpRequest(
|
||||
QUERY_CONTENT, RELAY_HOST, RELAY_PATH, mockCredentials, Optional.empty());
|
||||
// Input data passed to next handler
|
||||
assertThat(channel.writeInbound(inputBuffer)).isTrue();
|
||||
FullHttpRequest inputRequest = channel.readInbound();
|
||||
assertThat(inputRequest).isEqualTo(expectedRequest);
|
||||
// The channel is still open, and nothing else is to be read from it.
|
||||
assertThat((Object) channel.readInbound()).isNull();
|
||||
assertThat(channel.isActive()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_OutboundHttpResponseNotOK() {
|
||||
String outputString = "line1\r\nline2\r\n";
|
||||
|
||||
Reference in New Issue
Block a user