/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.http.client;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.junit.Rule;
import org.junit.Test;
import password.pwm.AppProperty;
import password.pwm.PwmConstants;
import password.pwm.config.Configuration;
import password.pwm.config.PwmSetting;
import password.pwm.config.stored.StoredConfigurationImpl;
import password.pwm.error.PwmUnrecoverableException;
import javax.net.ssl.SSLHandshakeException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.*;
public class PwmHttpClientTest {
@Rule public WireMockRule wm = new WireMockRule(wireMockConfig()
.dynamicPort()
.dynamicHttpsPort());
// Create a few mock objects, in case they're needed by the tests
private Configuration configuration = spy(new Configuration(StoredConfigurationImpl.newStoredConfiguration()));
private PwmHttpClientConfiguration pwmHttpClientConfiguration = mock(PwmHttpClientConfiguration.class);
public PwmHttpClientTest() throws PwmUnrecoverableException {
}
/**
* Test making a simple HTTP request from the client returned by PwmHttpClient.getHttpClient(...)
*/
@Test
public void testGetHttpClient_simpleHello() throws Exception {
// Stub out our local HTTP server
wm.stubFor(get(urlEqualTo("/simpleHello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello from the local mock server")));
// Obtain the HTTP client from PWM
HttpClient httpClient = PwmHttpClient.getHttpClient(configuration, pwmHttpClientConfiguration);
// Execute the HTTP request
HttpGet httpGet = new HttpGet(String.format("http://localhost:%d/simpleHello", wm.port()));
HttpResponse response = httpClient.execute(httpGet);
// Verify the response
int responseStatusCode = response.getStatusLine().getStatusCode();
assertThat(responseStatusCode).isEqualTo(200);
String responseContent = IOUtils.toString(response.getEntity().getContent());
assertThat(responseContent).startsWith("Hello");
// Verify the HTTP server got called as expected
wm.verify(getRequestedFor(urlEqualTo("/simpleHello"))
.withHeader("User-Agent", equalTo(PwmConstants.PWM_APP_NAME + " " + PwmConstants.SERVLET_VERSION)));
}
/**
* Test making an SSL request without setting SECURITY_HTTP_PROMISCUOUS_ENABLE to true, or supplying any certificates
*/
@Test(expected = SSLHandshakeException.class)
public void testGetHttpClient_sslHelloFail() throws Exception {
// Stub out our local HTTP server
wm.stubFor(get(urlEqualTo("/simpleHello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello from the local mock server")));
HttpClient httpClient = PwmHttpClient.getHttpClient(configuration, pwmHttpClientConfiguration);
HttpGet httpGet = new HttpGet(String.format("https://localhost:%d/simpleHello", wm.httpsPort()));
// This should throw an exception, since we're doing https without setting SECURITY_HTTP_PROMISCUOUS_ENABLE, or setting certificates
httpClient.execute(httpGet);
}
/**
* Test making an SSL request in promiscuous mode (no certificates needed)
*/
@Test
public void testGetHttpClient_sslHello() throws Exception {
// Stub out our local HTTP server
wm.stubFor(get(urlEqualTo("/simpleHello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello from the local mock server")));
// Stub out some mock object behavior
when(configuration.readAppProperty(AppProperty.SECURITY_HTTP_PROMISCUOUS_ENABLE)).thenReturn("true");
HttpClient httpClient = PwmHttpClient.getHttpClient(configuration, pwmHttpClientConfiguration);
HttpGet httpGet = new HttpGet(String.format("https://localhost:%d/simpleHello", wm.httpsPort()));
HttpResponse response = httpClient.execute(httpGet);
// Verify the response
int responseStatusCode = response.getStatusLine().getStatusCode();
assertThat(responseStatusCode).isEqualTo(200);
String responseContent = IOUtils.toString(response.getEntity().getContent());
assertThat(responseContent).startsWith("Hello");
}
/**
* Test making an SSL request using the server's certificate
*/
@Test
public void testGetHttpClient_sslWithCertificates() throws Exception {
// Stub out our local HTTP server
wm.stubFor(get(urlEqualTo("/simpleHello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello from the local mock server")));
// Stub out some mock object behavior
X509Certificate[] certificates = new X509Certificate[] { getWireMockSelfSignedCertificate() };
when(pwmHttpClientConfiguration.getCertificates()).thenReturn(certificates);
HttpClient httpClient = PwmHttpClient.getHttpClient(configuration, pwmHttpClientConfiguration);
HttpGet httpGet = new HttpGet(String.format("https://localhost:%d/simpleHello", wm.httpsPort()));
HttpResponse response = httpClient.execute(httpGet);
// Verify the response
int responseStatusCode = response.getStatusLine().getStatusCode();
assertThat(responseStatusCode).isEqualTo(200);
String responseContent = IOUtils.toString(response.getEntity().getContent());
assertThat(responseContent).startsWith("Hello");
}
/**
* Test making a request through a proxy
*/
@Test
public void testGetHttpClient_proxyHello() throws Exception {
// Stub out our local HTTP server
wm.stubFor(get(urlEqualTo("/simpleHello"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello from the local mock server")));
// Stub out some mock object behavior
when(configuration.readSettingAsString(PwmSetting.HTTP_PROXY_URL)).thenReturn(String.format("http://localhost:%d/simpleHello", wm.port()));
HttpClient httpClient = PwmHttpClient.getHttpClient(configuration, pwmHttpClientConfiguration);
// We are making a request to www.microfocus.com, but our server on localhost will receive it
HttpGet httpGet = new HttpGet(String.format("http://www.microfocus.com/simpleHello"));
HttpResponse response = httpClient.execute(httpGet);
// Verify the response
int responseStatusCode = response.getStatusLine().getStatusCode();
assertThat(responseStatusCode).isEqualTo(200);
String responseContent = IOUtils.toString(response.getEntity().getContent());
assertThat(responseContent).startsWith("Hello");
}
private X509Certificate getWireMockSelfSignedCertificate() {
InputStream keystoreInputStream = WireMock.class.getResourceAsStream("/keystore");
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(keystoreInputStream, "password".toCharArray());
return (X509Certificate) keyStore.getCertificate("wiremock");
} catch (Exception e) {
fail("Unable to load wiremock self-signed certificate", e);
}
return null;
}
}