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:
sarahcaseybot 2020-12-15 16:36:39 -05:00 committed by GitHub
parent 1db0c5b018
commit 08bb04128b
4 changed files with 163 additions and 20 deletions

View file

@ -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);
} }
} }

View file

@ -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();

View file

@ -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");

View file

@ -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.