Commit 58ee31b0 authored by Nils Bandener's avatar Nils Bandener
Browse files

Made MockIpdServer more resilient against port collisions

parent 816d329f
......@@ -36,7 +36,7 @@ public class HTTPJwtKeyByOpenIdConnectAuthenticatorTest {
@BeforeClass
public static void setUp() throws Exception {
mockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL);
mockIdpServer = MockIpdServer.start(TestJwk.Jwks.ALL);
}
@AfterClass
......
......@@ -47,7 +47,7 @@ public class KeySetRetrieverTest {
@BeforeClass
public static void setUp() throws Exception {
mockIdpServer = new MockIpdServer(TestJwk.Jwks.ALL);
mockIdpServer = MockIpdServer.start(TestJwk.Jwks.ALL);
}
@AfterClass
......
......@@ -20,6 +20,7 @@ import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.BindException;
import java.net.Socket;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
......@@ -58,152 +59,163 @@ import com.floragunn.searchguard.test.helper.file.FileHelper;
import com.floragunn.searchguard.test.helper.network.SocketUtils;
class MockIpdServer implements Closeable {
final static String CTX_DISCOVER = "/discover";
final static String CTX_KEYS = "/api/oauth/keys";
private final HttpServer httpServer;
private final int port;
private final String uri;
private final boolean ssl;
private final JsonWebKeys jwks;
MockIpdServer(JsonWebKeys jwks) throws IOException {
this(jwks, SocketUtils.findAvailableTcpPort(), false);
}
MockIpdServer(JsonWebKeys jwks, int port, boolean ssl) throws IOException {
this.port = port;
this.uri = (ssl ? "https" : "http") + "://localhost:" + port;
this.ssl = ssl;
this.jwks = jwks;
ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap().setListenerPort(port)
.registerHandler(CTX_DISCOVER, new HttpRequestHandler() {
@Override
public void handle(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
handleDiscoverRequest(request, response, context);
}
}).registerHandler(CTX_KEYS, new HttpRequestHandler() {
@Override
public void handle(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
handleKeysRequest(request, response, context);
}
});
if (ssl) {
serverBootstrap = serverBootstrap.setSslContext(createSSLContext())
.setSslSetupHandler(new SSLServerSetupHandler() {
@Override
public void initialize(SSLServerSocket socket) throws SSLException {
socket.setNeedClientAuth(true);
}
}).setConnectionFactory(new HttpConnectionFactory<DefaultBHttpServerConnection>() {
private ConnectionConfig cconfig = ConnectionConfig.DEFAULT;
@Override
public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException {
final SSLTestHttpServerConnection conn = new SSLTestHttpServerConnection(
this.cconfig.getBufferSize(), this.cconfig.getFragmentSizeHint(),
ConnSupport.createDecoder(this.cconfig), ConnSupport.createEncoder(this.cconfig),
this.cconfig.getMessageConstraints(), null, null, null, null);
conn.bind(socket);
return conn;
}
});
}
this.httpServer = serverBootstrap.create();
httpServer.start();
}
@Override
public void close() throws IOException {
httpServer.stop();
}
public HttpServer getHttpServer() {
return httpServer;
}
public String getUri() {
return uri;
}
public String getDiscoverUri() {
return uri + CTX_DISCOVER;
}
public int getPort() {
return port;
}
protected void handleDiscoverRequest(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
response.setStatusCode(200);
response.setHeader("Cache-Control", "public, max-age=31536000");
response.setEntity(new StringEntity("{\"jwks_uri\": \"" + uri + CTX_KEYS + "\",\n" + "\"issuer\": \"" + uri
+ "\", \"unknownPropertyToBeIgnored\": 42}"));
}
protected void handleKeysRequest(HttpRequest request, HttpResponse response, HttpContext context)
throws HttpException, IOException {
response.setStatusCode(200);
response.setEntity(new StringEntity(toJson(jwks)));
}
private SSLContext createSSLContext() {
if (!this.ssl) {
return null;
}
try {
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
final KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream trustStream = new FileInputStream(
FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile());
trustStore.load(trustStream, "changeit".toCharArray());
tmf.init(trustStore);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStream = new FileInputStream(
FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile());
keyStore.load(keyStream, "changeit".toCharArray());
kmf.init(keyStore, "changeit".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
}
static class SSLTestHttpServerConnection extends DefaultBHttpServerConnection {
public SSLTestHttpServerConnection(final int buffersize, final int fragmentSizeHint,
final CharsetDecoder chardecoder, final CharsetEncoder charencoder,
final MessageConstraints constraints, final ContentLengthStrategy incomingContentStrategy,
final ContentLengthStrategy outgoingContentStrategy,
final HttpMessageParserFactory<HttpRequest> requestParserFactory,
final HttpMessageWriterFactory<HttpResponse> responseWriterFactory) {
super(buffersize, fragmentSizeHint, chardecoder, charencoder, constraints, incomingContentStrategy,
outgoingContentStrategy, requestParserFactory, responseWriterFactory);
}
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
return ((SSLSocket) getSocket()).getSession().getPeerCertificates();
}
}
final static String CTX_DISCOVER = "/discover";
final static String CTX_KEYS = "/api/oauth/keys";
private final HttpServer httpServer;
private final int port;
private final String uri;
private final boolean ssl;
private final JsonWebKeys jwks;
static MockIpdServer start(JsonWebKeys jwks) throws IOException {
int i = 0;
for (;;) {
try {
return new MockIpdServer(jwks);
} catch (BindException e) {
if (i >= 10) {
throw e;
} else {
i++;
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
throw new RuntimeException(e1);
}
}
}
}
}
MockIpdServer(JsonWebKeys jwks) throws IOException {
this(jwks, SocketUtils.findAvailableTcpPort(), false);
}
MockIpdServer(JsonWebKeys jwks, int port, boolean ssl) throws IOException {
this.port = port;
this.uri = (ssl ? "https" : "http") + "://localhost:" + port;
this.ssl = ssl;
this.jwks = jwks;
ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap().setListenerPort(port).registerHandler(CTX_DISCOVER, new HttpRequestHandler() {
@Override
public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
handleDiscoverRequest(request, response, context);
}
}).registerHandler(CTX_KEYS, new HttpRequestHandler() {
@Override
public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
handleKeysRequest(request, response, context);
}
});
if (ssl) {
serverBootstrap = serverBootstrap.setSslContext(createSSLContext()).setSslSetupHandler(new SSLServerSetupHandler() {
@Override
public void initialize(SSLServerSocket socket) throws SSLException {
socket.setNeedClientAuth(true);
}
}).setConnectionFactory(new HttpConnectionFactory<DefaultBHttpServerConnection>() {
private ConnectionConfig cconfig = ConnectionConfig.DEFAULT;
@Override
public DefaultBHttpServerConnection createConnection(final Socket socket) throws IOException {
final SSLTestHttpServerConnection conn = new SSLTestHttpServerConnection(this.cconfig.getBufferSize(),
this.cconfig.getFragmentSizeHint(), ConnSupport.createDecoder(this.cconfig), ConnSupport.createEncoder(this.cconfig),
this.cconfig.getMessageConstraints(), null, null, null, null);
conn.bind(socket);
return conn;
}
});
}
this.httpServer = serverBootstrap.create();
httpServer.start();
}
@Override
public void close() throws IOException {
httpServer.stop();
}
public HttpServer getHttpServer() {
return httpServer;
}
public String getUri() {
return uri;
}
public String getDiscoverUri() {
return uri + CTX_DISCOVER;
}
public int getPort() {
return port;
}
protected void handleDiscoverRequest(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
response.setStatusCode(200);
response.setHeader("Cache-Control", "public, max-age=31536000");
response.setEntity(new StringEntity(
"{\"jwks_uri\": \"" + uri + CTX_KEYS + "\",\n" + "\"issuer\": \"" + uri + "\", \"unknownPropertyToBeIgnored\": 42}"));
}
protected void handleKeysRequest(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
response.setStatusCode(200);
response.setEntity(new StringEntity(toJson(jwks)));
}
private SSLContext createSSLContext() {
if (!this.ssl) {
return null;
}
try {
final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
final KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream trustStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/truststore.jks").toFile());
trustStore.load(trustStream, "changeit".toCharArray());
tmf.init(trustStore);
final KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
final KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyStream = new FileInputStream(FileHelper.getAbsoluteFilePathFromClassPath("jwt/node-0-keystore.jks").toFile());
keyStore.load(keyStream, "changeit".toCharArray());
kmf.init(keyStore, "changeit".toCharArray());
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
}
static class SSLTestHttpServerConnection extends DefaultBHttpServerConnection {
public SSLTestHttpServerConnection(final int buffersize, final int fragmentSizeHint, final CharsetDecoder chardecoder,
final CharsetEncoder charencoder, final MessageConstraints constraints, final ContentLengthStrategy incomingContentStrategy,
final ContentLengthStrategy outgoingContentStrategy, final HttpMessageParserFactory<HttpRequest> requestParserFactory,
final HttpMessageWriterFactory<HttpResponse> responseWriterFactory) {
super(buffersize, fragmentSizeHint, chardecoder, charencoder, constraints, incomingContentStrategy, outgoingContentStrategy,
requestParserFactory, responseWriterFactory);
}
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
return ((SSLSocket) getSocket()).getSession().getPeerCertificates();
}
}
}
......@@ -28,7 +28,7 @@ public class SingleKeyHTTPJwtKeyByOpenIdConnectAuthenticatorTest {
@Test
public void basicTest() throws Exception {
MockIpdServer mockIdpServer = new MockIpdServer(TestJwk.Jwks.RSA_1);
MockIpdServer mockIdpServer = MockIpdServer.start(TestJwk.Jwks.RSA_1);
try {
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment