mirror of
https://github.com/google/nomulus.git
synced 2025-04-30 12:07:51 +02:00
Modify proxy to pass full certificate before login (#896)
* Modify proxy to pass full certificate until partner is logged in * refactor tests * revert package-lock.json * add sample cert string to tests
This commit is contained in:
parent
1db0c5b018
commit
08bb04128b
4 changed files with 163 additions and 20 deletions
|
@ -34,7 +34,9 @@ import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
|
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import java.security.cert.CertificateEncodingException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/** Handler that processes EPP protocol logic. */
|
/** Handler that processes EPP protocol logic. */
|
||||||
|
@ -52,18 +54,26 @@ public class EppServiceHandler extends HttpsRelayServiceHandler {
|
||||||
/** Name of the HTTP header that stores the client certificate hash. */
|
/** Name of the HTTP header that stores the client certificate hash. */
|
||||||
public static final String SSL_CLIENT_CERTIFICATE_HASH_FIELD = "X-SSL-Certificate";
|
public static final String SSL_CLIENT_CERTIFICATE_HASH_FIELD = "X-SSL-Certificate";
|
||||||
|
|
||||||
|
/** Name of the HTTP header that stores the full client certificate. */
|
||||||
|
public static final String SSL_CLIENT_FULL_CERTIFICATE_FIELD = "X-SSL-Full-Certificate";
|
||||||
|
|
||||||
/** Name of the HTTP header that stores the client IP address. */
|
/** Name of the HTTP header that stores the client IP address. */
|
||||||
public static final String FORWARDED_FOR_FIELD = "X-Forwarded-For";
|
public static final String FORWARDED_FOR_FIELD = "X-Forwarded-For";
|
||||||
|
|
||||||
/** Name of the HTTP header that indicates if the EPP session should be closed. */
|
/** Name of the HTTP header that indicates if the EPP session should be closed. */
|
||||||
public static final String EPP_SESSION_FIELD = "Epp-Session";
|
public static final String EPP_SESSION_FIELD = "Epp-Session";
|
||||||
|
|
||||||
|
/** Name of the HTTP header that indicates a successful login has occurred. */
|
||||||
|
public static final String EPP_LOGGED_IN_FIELD = "Logged-In";
|
||||||
|
|
||||||
public static final String EPP_CONTENT_TYPE = "application/epp+xml";
|
public static final String EPP_CONTENT_TYPE = "application/epp+xml";
|
||||||
|
|
||||||
private final byte[] helloBytes;
|
private final byte[] helloBytes;
|
||||||
|
|
||||||
private String sslClientCertificateHash;
|
private String sslClientCertificateHash;
|
||||||
|
private X509Certificate sslClientCertificate;
|
||||||
private String clientAddress;
|
private String clientAddress;
|
||||||
|
private boolean isLoggedIn = false;
|
||||||
|
|
||||||
public EppServiceHandler(
|
public EppServiceHandler(
|
||||||
String relayHost,
|
String relayHost,
|
||||||
|
@ -103,7 +113,8 @@ public class EppServiceHandler extends HttpsRelayServiceHandler {
|
||||||
.addListener(
|
.addListener(
|
||||||
(Promise<X509Certificate> promise) -> {
|
(Promise<X509Certificate> promise) -> {
|
||||||
if (promise.isSuccess()) {
|
if (promise.isSuccess()) {
|
||||||
sslClientCertificateHash = getCertificateHash(promise.get());
|
sslClientCertificate = promise.get();
|
||||||
|
sslClientCertificateHash = getCertificateHash(sslClientCertificate);
|
||||||
// Set the client cert hash key attribute for both this channel,
|
// Set the client cert hash key attribute for both this channel,
|
||||||
// used for collecting metrics on specific clients.
|
// used for collecting metrics on specific clients.
|
||||||
ctx.channel().attr(CLIENT_CERTIFICATE_HASH_KEY).set(sslClientCertificateHash);
|
ctx.channel().attr(CLIENT_CERTIFICATE_HASH_KEY).set(sslClientCertificateHash);
|
||||||
|
@ -132,6 +143,17 @@ public class EppServiceHandler extends HttpsRelayServiceHandler {
|
||||||
.set(FORWARDED_FOR_FIELD, clientAddress)
|
.set(FORWARDED_FOR_FIELD, clientAddress)
|
||||||
.set(HttpHeaderNames.CONTENT_TYPE, EPP_CONTENT_TYPE)
|
.set(HttpHeaderNames.CONTENT_TYPE, EPP_CONTENT_TYPE)
|
||||||
.set(HttpHeaderNames.ACCEPT, EPP_CONTENT_TYPE);
|
.set(HttpHeaderNames.ACCEPT, EPP_CONTENT_TYPE);
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
try {
|
||||||
|
request
|
||||||
|
.headers()
|
||||||
|
.set(
|
||||||
|
SSL_CLIENT_FULL_CERTIFICATE_FIELD,
|
||||||
|
Base64.getEncoder().encodeToString(sslClientCertificate.getEncoded()));
|
||||||
|
} catch (CertificateEncodingException e) {
|
||||||
|
throw new RuntimeException("Cannot encode client certificate", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,9 +163,13 @@ public class EppServiceHandler extends HttpsRelayServiceHandler {
|
||||||
checkArgument(msg instanceof HttpResponse);
|
checkArgument(msg instanceof HttpResponse);
|
||||||
HttpResponse response = (HttpResponse) msg;
|
HttpResponse response = (HttpResponse) msg;
|
||||||
String sessionAliveValue = response.headers().get(EPP_SESSION_FIELD);
|
String sessionAliveValue = response.headers().get(EPP_SESSION_FIELD);
|
||||||
|
String loginValue = response.headers().get(EPP_LOGGED_IN_FIELD);
|
||||||
if (sessionAliveValue != null && sessionAliveValue.equals("close")) {
|
if (sessionAliveValue != null && sessionAliveValue.equals("close")) {
|
||||||
promise.addListener(ChannelFutureListener.CLOSE);
|
promise.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
|
if (loginValue != null && loginValue.equals("true")) {
|
||||||
|
isLoggedIn = true;
|
||||||
|
}
|
||||||
super.write(ctx, msg, promise);
|
super.write(ctx, msg, promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.proxy;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
|
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
|
||||||
|
import static google.registry.proxy.TestUtils.SAMPLE_CERT;
|
||||||
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
|
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
|
||||||
import static google.registry.util.ResourceUtils.readResourceBytes;
|
import static google.registry.util.ResourceUtils.readResourceBytes;
|
||||||
import static google.registry.util.X509Utils.getCertificateHash;
|
import static google.registry.util.X509Utils.getCertificateHash;
|
||||||
|
@ -25,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
import google.registry.proxy.handler.HttpsRelayServiceHandler.NonOkHttpResponseException;
|
||||||
import google.registry.testing.FakeClock;
|
import google.registry.testing.FakeClock;
|
||||||
import google.registry.util.SelfSignedCaCertificate;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
@ -36,6 +36,8 @@ import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.cookie.Cookie;
|
import io.netty.handler.codec.http.cookie.Cookie;
|
||||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -96,8 +98,8 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FullHttpRequest makeEppHttpRequest(byte[] content, Cookie... cookies) {
|
private FullHttpRequest makeEppHttpRequestWithCertificate(byte[] content, Cookie... cookies) {
|
||||||
return TestUtils.makeEppHttpRequest(
|
return TestUtils.makeEppHttpRequestWithCertificate(
|
||||||
new String(content, UTF_8),
|
new String(content, UTF_8),
|
||||||
PROXY_CONFIG.epp.relayHost,
|
PROXY_CONFIG.epp.relayHost,
|
||||||
PROXY_CONFIG.epp.relayPath,
|
PROXY_CONFIG.epp.relayPath,
|
||||||
|
@ -120,7 +122,10 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
@Override
|
@Override
|
||||||
void beforeEach() throws Exception {
|
void beforeEach() throws Exception {
|
||||||
testComponent = makeTestComponent(new FakeClock());
|
testComponent = makeTestComponent(new FakeClock());
|
||||||
certificate = SelfSignedCaCertificate.create().cert();
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
certificate =
|
||||||
|
(X509Certificate)
|
||||||
|
cf.generateCertificate(new ByteArrayInputStream(SAMPLE_CERT.getBytes(UTF_8)));
|
||||||
initializeChannel(
|
initializeChannel(
|
||||||
ch -> {
|
ch -> {
|
||||||
ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS);
|
ch.attr(REMOTE_ADDRESS_KEY).set(CLIENT_ADDRESS);
|
||||||
|
@ -134,13 +139,15 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_singleFrameInboundMessage() throws Exception {
|
void testSuccess_singleFrameInboundMessage() throws Exception {
|
||||||
// First inbound message is hello.
|
// First inbound message is hello.
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(HELLO_BYTES));
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
|
.isEqualTo(makeEppHttpRequestWithCertificate(HELLO_BYTES));
|
||||||
|
|
||||||
byte[] inputBytes = readResourceBytes(getClass(), "login.xml").read();
|
byte[] inputBytes = readResourceBytes(getClass(), "login.xml").read();
|
||||||
|
|
||||||
// Verify inbound message is as expected.
|
// Verify inbound message is as expected.
|
||||||
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes))).isTrue();
|
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes))).isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes));
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes));
|
||||||
|
|
||||||
// Nothing more to read.
|
// Nothing more to read.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
|
@ -161,8 +168,10 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
Unpooled.wrappedBuffer(
|
Unpooled.wrappedBuffer(
|
||||||
getByteBufFromContent(inputBytes1), getByteBufFromContent(inputBytes2))))
|
getByteBufFromContent(inputBytes1), getByteBufFromContent(inputBytes2))))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes1));
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes2));
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1));
|
||||||
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2));
|
||||||
|
|
||||||
// Nothing more to read.
|
// Nothing more to read.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
|
@ -186,11 +195,13 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
|
|
||||||
// The second frame contains the first message, and part of the second message.
|
// The second frame contains the first message, and part of the second message.
|
||||||
assertThat(channel.writeInbound(inputBuffer.readBytes(inputBytes2.length))).isTrue();
|
assertThat(channel.writeInbound(inputBuffer.readBytes(inputBytes2.length))).isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes1));
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1));
|
||||||
|
|
||||||
// The third frame contains the rest of the second message.
|
// The third frame contains the rest of the second message.
|
||||||
assertThat(channel.writeInbound(inputBuffer)).isTrue();
|
assertThat(channel.writeInbound(inputBuffer)).isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound()).isEqualTo(makeEppHttpRequest(inputBytes2));
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2));
|
||||||
|
|
||||||
// Nothing more to read.
|
// Nothing more to read.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
|
@ -252,7 +263,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
byte[] inputBytes1 = readResourceBytes(getClass(), "logout.xml").read();
|
byte[] inputBytes1 = readResourceBytes(getClass(), "logout.xml").read();
|
||||||
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes1))).isTrue();
|
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes1))).isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound())
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
.isEqualTo(makeEppHttpRequest(inputBytes1, cookie1, cookie2));
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes1, cookie1, cookie2));
|
||||||
|
|
||||||
// Second outbound message change cookies.
|
// Second outbound message change cookies.
|
||||||
byte[] outputBytes2 = readResourceBytes(getClass(), "logout_response.xml").read();
|
byte[] outputBytes2 = readResourceBytes(getClass(), "logout_response.xml").read();
|
||||||
|
@ -267,7 +278,7 @@ class EppProtocolModuleTest extends ProtocolModuleTest {
|
||||||
byte[] inputBytes2 = readResourceBytes(getClass(), "login.xml").read();
|
byte[] inputBytes2 = readResourceBytes(getClass(), "login.xml").read();
|
||||||
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes2))).isTrue();
|
assertThat(channel.writeInbound(getByteBufFromContent(inputBytes2))).isTrue();
|
||||||
assertThat((FullHttpRequest) channel.readInbound())
|
assertThat((FullHttpRequest) channel.readInbound())
|
||||||
.isEqualTo(makeEppHttpRequest(inputBytes2, cookie1, cookie2, cookie3));
|
.isEqualTo(makeEppHttpRequestWithCertificate(inputBytes2, cookie1, cookie2, cookie3));
|
||||||
|
|
||||||
// Nothing more to write or read.
|
// Nothing more to write or read.
|
||||||
assertThat((Object) channel.readOutbound()).isNull();
|
assertThat((Object) channel.readOutbound()).isNull();
|
||||||
|
|
|
@ -36,6 +36,54 @@ import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
|
||||||
/** Utility class for various helper methods used in testing. */
|
/** Utility class for various helper methods used in testing. */
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
|
|
||||||
|
public static final String SAMPLE_CERT =
|
||||||
|
"-----BEGIN CERTIFICATE-----\n"
|
||||||
|
+ "MIIDvTCCAqWgAwIBAgIJAK/PgPT0jTwRMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV\n"
|
||||||
|
+ "BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxDzAN\n"
|
||||||
|
+ "BgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWluLXJlZ2lzdHJ5LXRlc3QxEDAO\n"
|
||||||
|
+ "BgNVBAMMB2NsaWVudDEwHhcNMTUwODI2MTkxODA4WhcNNDMwMTExMTkxODA4WjB1\n"
|
||||||
|
+ "MQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZ\n"
|
||||||
|
+ "b3JrMQ8wDQYDVQQKDAZHb29nbGUxHTAbBgNVBAsMFGRvbWFpbi1yZWdpc3RyeS10\n"
|
||||||
|
+ "ZXN0MRAwDgYDVQQDDAdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n"
|
||||||
|
+ "CgKCAQEAvoE/IoFJyzb0dU4NFhL8FYgy+B/GnUd5aA66CMx5xKRMbEAtIgxU8TTO\n"
|
||||||
|
+ "W+9jdTsE00Grk3Ct4KdY73CYW+6IFXL4O0K/m5S+uajh+I2UMVZJV38RAIqNxue0\n"
|
||||||
|
+ "Egv9M4haSsCVIPcX9b+6McywfYSF1bzPb2Gb2FAQO7Jb0BjlPhPMIROCrbG40qPg\n"
|
||||||
|
+ "LWrl33dz+O52kO+DyZEzHqI55xH6au77sMITsJe+X23lzQcMFUUm8moiOw0EKrj/\n"
|
||||||
|
+ "GaMTZLHP46BCRoJDAPTNx55seIwgAHbKA2VVtqrvmA2XYJQA6ipdhfKRoJFy8Z8H\n"
|
||||||
|
+ "DYsorGtazQL2HhF/5uJD25z1m5eQHQIDAQABo1AwTjAdBgNVHQ4EFgQUParEmiSR\n"
|
||||||
|
+ "U/Oqy8hr7k+MBKhZwVkwHwYDVR0jBBgwFoAUParEmiSRU/Oqy8hr7k+MBKhZwVkw\n"
|
||||||
|
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAojsUhF6PtZrStnHBFWNR\n"
|
||||||
|
+ "ryzvANB8krZlYeX9Hkqn8zIVfAkpbVmL8aZQ7yj17jSpw47PQh3x5gwA9yc/SS0G\n"
|
||||||
|
+ "E1rGuxYH02UGbua8G0+vviSQfLtskPQzK7EIR63WNhHEo/Q9umLJkZ0LguWEBf3L\n"
|
||||||
|
+ "q8CoXv2i/RNvqVPcTNp/zCKXJZAa8wAjNRJs834AZj4k5xwyYZ3F8D5PGz+YMOmV\n"
|
||||||
|
+ "M9Qd+NdXSC/Qn7HQzFhE8p5elBV35P8oX5dXEfn0S7zOXDenp5JvvLoggOWOcKsq\n"
|
||||||
|
+ "KiWDQrsT+TMKmHL94/h4t7FghtQLMzY5SGYJsYTv/LG8tewrz6KRb/Wj3JNojyEw\n"
|
||||||
|
+ "Ug==\n"
|
||||||
|
+ "-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
public static final String SAMPLE_CERT_ENCODED =
|
||||||
|
"MIIDvTCCAqWgAwIBAgIJAK/PgPT0jTwRMA0GCSqGSIb3DQEBCwUAMHUxCzAJBgNV"
|
||||||
|
+ "BAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3IFlvcmsxDzAN"
|
||||||
|
+ "BgNVBAoMBkdvb2dsZTEdMBsGA1UECwwUZG9tYWluLXJlZ2lzdHJ5LXRlc3QxEDAO"
|
||||||
|
+ "BgNVBAMMB2NsaWVudDEwHhcNMTUwODI2MTkxODA4WhcNNDMwMTExMTkxODA4WjB1"
|
||||||
|
+ "MQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZ"
|
||||||
|
+ "b3JrMQ8wDQYDVQQKDAZHb29nbGUxHTAbBgNVBAsMFGRvbWFpbi1yZWdpc3RyeS10"
|
||||||
|
+ "ZXN0MRAwDgYDVQQDDAdjbGllbnQxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB"
|
||||||
|
+ "CgKCAQEAvoE/IoFJyzb0dU4NFhL8FYgy+B/GnUd5aA66CMx5xKRMbEAtIgxU8TTO"
|
||||||
|
+ "W+9jdTsE00Grk3Ct4KdY73CYW+6IFXL4O0K/m5S+uajh+I2UMVZJV38RAIqNxue0"
|
||||||
|
+ "Egv9M4haSsCVIPcX9b+6McywfYSF1bzPb2Gb2FAQO7Jb0BjlPhPMIROCrbG40qPg"
|
||||||
|
+ "LWrl33dz+O52kO+DyZEzHqI55xH6au77sMITsJe+X23lzQcMFUUm8moiOw0EKrj/"
|
||||||
|
+ "GaMTZLHP46BCRoJDAPTNx55seIwgAHbKA2VVtqrvmA2XYJQA6ipdhfKRoJFy8Z8H"
|
||||||
|
+ "DYsorGtazQL2HhF/5uJD25z1m5eQHQIDAQABo1AwTjAdBgNVHQ4EFgQUParEmiSR"
|
||||||
|
+ "U/Oqy8hr7k+MBKhZwVkwHwYDVR0jBBgwFoAUParEmiSRU/Oqy8hr7k+MBKhZwVkw"
|
||||||
|
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAojsUhF6PtZrStnHBFWNR"
|
||||||
|
+ "ryzvANB8krZlYeX9Hkqn8zIVfAkpbVmL8aZQ7yj17jSpw47PQh3x5gwA9yc/SS0G"
|
||||||
|
+ "E1rGuxYH02UGbua8G0+vviSQfLtskPQzK7EIR63WNhHEo/Q9umLJkZ0LguWEBf3L"
|
||||||
|
+ "q8CoXv2i/RNvqVPcTNp/zCKXJZAa8wAjNRJs834AZj4k5xwyYZ3F8D5PGz+YMOmV"
|
||||||
|
+ "M9Qd+NdXSC/Qn7HQzFhE8p5elBV35P8oX5dXEfn0S7zOXDenp5JvvLoggOWOcKsq"
|
||||||
|
+ "KiWDQrsT+TMKmHL94/h4t7FghtQLMzY5SGYJsYTv/LG8tewrz6KRb/Wj3JNojyEw"
|
||||||
|
+ "Ug==";
|
||||||
|
|
||||||
public static FullHttpRequest makeHttpPostRequest(String content, String host, String path) {
|
public static FullHttpRequest makeHttpPostRequest(String content, String host, String path) {
|
||||||
ByteBuf buf = Unpooled.wrappedBuffer(content.getBytes(US_ASCII));
|
ByteBuf buf = Unpooled.wrappedBuffer(content.getBytes(US_ASCII));
|
||||||
FullHttpRequest request =
|
FullHttpRequest request =
|
||||||
|
@ -101,6 +149,21 @@ public class TestUtils {
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FullHttpRequest makeEppHttpRequestWithCertificate(
|
||||||
|
String content,
|
||||||
|
String host,
|
||||||
|
String path,
|
||||||
|
String accessToken,
|
||||||
|
String sslClientCertificateHash,
|
||||||
|
String clientAddress,
|
||||||
|
Cookie... cookies) {
|
||||||
|
FullHttpRequest request =
|
||||||
|
makeEppHttpRequest(
|
||||||
|
content, host, path, accessToken, sslClientCertificateHash, clientAddress, cookies);
|
||||||
|
request.headers().set("X-SSL-Full-Certificate", SAMPLE_CERT_ENCODED);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
public static FullHttpResponse makeWhoisHttpResponse(String content, HttpResponseStatus status) {
|
public static FullHttpResponse makeWhoisHttpResponse(String content, HttpResponseStatus status) {
|
||||||
FullHttpResponse response = makeHttpResponse(content, status);
|
FullHttpResponse response = makeHttpResponse(content, status);
|
||||||
response.headers().set("content-type", "text/plain");
|
response.headers().set("content-type", "text/plain");
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.proxy.handler;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
|
import static google.registry.networking.handler.SslServerInitializer.CLIENT_CERTIFICATE_PROMISE_KEY;
|
||||||
|
import static google.registry.proxy.TestUtils.SAMPLE_CERT;
|
||||||
import static google.registry.proxy.TestUtils.assertHttpRequestEquivalent;
|
import static google.registry.proxy.TestUtils.assertHttpRequestEquivalent;
|
||||||
import static google.registry.proxy.TestUtils.makeEppHttpResponse;
|
import static google.registry.proxy.TestUtils.makeEppHttpResponse;
|
||||||
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
|
import static google.registry.proxy.handler.ProxyProtocolHandler.REMOTE_ADDRESS_KEY;
|
||||||
|
@ -43,6 +44,8 @@ import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.cookie.Cookie;
|
import io.netty.handler.codec.http.cookie.Cookie;
|
||||||
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
import io.netty.handler.codec.http.cookie.DefaultCookie;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -109,9 +112,23 @@ class EppServiceHandlerTest {
|
||||||
cookies);
|
cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FullHttpRequest makeEppHttpRequestWithCertificate(String content, Cookie... cookies) {
|
||||||
|
return TestUtils.makeEppHttpRequestWithCertificate(
|
||||||
|
content,
|
||||||
|
RELAY_HOST,
|
||||||
|
RELAY_PATH,
|
||||||
|
ACCESS_TOKEN,
|
||||||
|
getCertificateHash(clientCertificate),
|
||||||
|
CLIENT_ADDRESS,
|
||||||
|
cookies);
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() throws Exception {
|
void beforeEach() throws Exception {
|
||||||
clientCertificate = SelfSignedCaCertificate.create().cert();
|
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
clientCertificate =
|
||||||
|
(X509Certificate)
|
||||||
|
cf.generateCertificate(new ByteArrayInputStream(SAMPLE_CERT.getBytes(UTF_8)));
|
||||||
channel = setUpNewChannel(eppServiceHandler);
|
channel = setUpNewChannel(eppServiceHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +211,7 @@ class EppServiceHandlerTest {
|
||||||
setHandshakeSuccess();
|
setHandshakeSuccess();
|
||||||
// hello bytes should be passed to the next handler.
|
// hello bytes should be passed to the next handler.
|
||||||
FullHttpRequest helloRequest = channel.readInbound();
|
FullHttpRequest helloRequest = channel.readInbound();
|
||||||
assertThat(helloRequest).isEqualTo(makeEppHttpRequest(HELLO));
|
assertThat(helloRequest).isEqualTo(makeEppHttpRequestWithCertificate(HELLO));
|
||||||
// Nothing further to pass to the next handler.
|
// Nothing further to pass to the next handler.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
assertThat(channel.isActive()).isTrue();
|
assertThat(channel.isActive()).isTrue();
|
||||||
|
@ -216,12 +233,34 @@ class EppServiceHandlerTest {
|
||||||
String content = "<epp>stuff</epp>";
|
String content = "<epp>stuff</epp>";
|
||||||
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
|
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
|
||||||
FullHttpRequest request = channel.readInbound();
|
FullHttpRequest request = channel.readInbound();
|
||||||
assertThat(request).isEqualTo(makeEppHttpRequest(content));
|
assertThat(request).isEqualTo(makeEppHttpRequestWithCertificate(content));
|
||||||
// Nothing further to pass to the next handler.
|
// Nothing further to pass to the next handler.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
assertThat(channel.isActive()).isTrue();
|
assertThat(channel.isActive()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSuccess_sendCertificateOnlyBeforeLogin() throws Exception {
|
||||||
|
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(makeEppHttpRequestWithCertificate(content));
|
||||||
|
// Receive response indicating session is logged in
|
||||||
|
HttpResponse response = makeEppHttpResponse(content, HttpResponseStatus.OK);
|
||||||
|
response.headers().set("Logged-In", "true");
|
||||||
|
// Send another inbound message after login
|
||||||
|
channel.writeOutbound(response);
|
||||||
|
channel.writeInbound(Unpooled.wrappedBuffer(content.getBytes(UTF_8)));
|
||||||
|
request = channel.readInbound();
|
||||||
|
// Second request should not have full certificate
|
||||||
|
assertThat(request).isEqualTo(makeEppHttpRequest(content));
|
||||||
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
|
assertThat(channel.isActive()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testSuccess_sendResponseToNextHandler() throws Exception {
|
void testSuccess_sendResponseToNextHandler() throws Exception {
|
||||||
setHandshakeSuccess();
|
setHandshakeSuccess();
|
||||||
|
@ -294,7 +333,8 @@ class EppServiceHandlerTest {
|
||||||
String requestContent = "<epp>request</epp>";
|
String requestContent = "<epp>request</epp>";
|
||||||
channel.writeInbound(Unpooled.wrappedBuffer(requestContent.getBytes(UTF_8)));
|
channel.writeInbound(Unpooled.wrappedBuffer(requestContent.getBytes(UTF_8)));
|
||||||
FullHttpRequest request = channel.readInbound();
|
FullHttpRequest request = channel.readInbound();
|
||||||
assertHttpRequestEquivalent(request, makeEppHttpRequest(requestContent, cookie1, cookie2));
|
assertHttpRequestEquivalent(
|
||||||
|
request, makeEppHttpRequestWithCertificate(requestContent, cookie1, cookie2));
|
||||||
// Nothing further to pass to the next handler.
|
// Nothing further to pass to the next handler.
|
||||||
assertThat((Object) channel.readInbound()).isNull();
|
assertThat((Object) channel.readInbound()).isNull();
|
||||||
assertThat((Object) channel.readOutbound()).isNull();
|
assertThat((Object) channel.readOutbound()).isNull();
|
||||||
|
@ -317,13 +357,16 @@ class EppServiceHandlerTest {
|
||||||
// First request written.
|
// First request written.
|
||||||
channel.writeInbound(Unpooled.wrappedBuffer(requestContent1.getBytes(UTF_8)));
|
channel.writeInbound(Unpooled.wrappedBuffer(requestContent1.getBytes(UTF_8)));
|
||||||
FullHttpRequest request1 = channel.readInbound();
|
FullHttpRequest request1 = channel.readInbound();
|
||||||
assertHttpRequestEquivalent(request1, makeEppHttpRequest(requestContent1, cookie1, cookie2));
|
assertHttpRequestEquivalent(
|
||||||
|
request1, makeEppHttpRequestWithCertificate(requestContent1, cookie1, cookie2));
|
||||||
String responseContent2 = "<epp>response2</epp>";
|
String responseContent2 = "<epp>response2</epp>";
|
||||||
Cookie cookie3 = new DefaultCookie("name3", "value3");
|
Cookie cookie3 = new DefaultCookie("name3", "value3");
|
||||||
Cookie newCookie2 = new DefaultCookie("name2", "newValue");
|
Cookie newCookie2 = new DefaultCookie("name2", "newValue");
|
||||||
// Second response written.
|
// Second response written.
|
||||||
channel.writeOutbound(
|
HttpResponse response =
|
||||||
makeEppHttpResponse(responseContent2, HttpResponseStatus.OK, cookie3, newCookie2));
|
makeEppHttpResponse(responseContent2, HttpResponseStatus.OK, cookie3, newCookie2);
|
||||||
|
response.headers().set("Logged-In", "true");
|
||||||
|
channel.writeOutbound(response);
|
||||||
channel.readOutbound();
|
channel.readOutbound();
|
||||||
String requestContent2 = "<epp>request2</epp>";
|
String requestContent2 = "<epp>request2</epp>";
|
||||||
// Second request written.
|
// Second request written.
|
||||||
|
|
Loading…
Add table
Reference in a new issue