// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.io.remotecontrol;
import static org.junit.Assert.assertEquals;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openstreetmap.josm.JOSMFixture;
import org.openstreetmap.josm.Main;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Unit tests for Remote Control
*/
public class RemoteControlTest {
private String httpBase;
private String httpsBase;
/**
* Starts Remote control before testing requests.
* @throws GeneralSecurityException if a security error occurs
*/
@Before
public void setUp() throws GeneralSecurityException {
JOSMFixture.createUnitTestFixture().init();
RemoteControl.PROP_REMOTECONTROL_HTTPS_ENABLED.put(true);
deleteKeystore();
RemoteControl.start();
disableCertificateValidation();
httpBase = "http://127.0.0.1:"+Main.pref.getInteger("remote.control.port", 8111);
httpsBase = "https://127.0.0.1:"+Main.pref.getInteger("remote.control.https.port", 8112);
}
/**
* Deletes JOSM keystore, if it exists.
*/
public static void deleteKeystore() {
try {
Files.deleteIfExists(Paths.get(
RemoteControl.getRemoteControlDir()).resolve(RemoteControlHttpsServer.KEYSTORE_FILENAME));
} catch (IOException e) {
Main.error(e);
}
}
/**
* Disable all HTTPS validation mechanisms as described
* <a href="http://stackoverflow.com/a/2893932/2257172">here</a> and
* <a href="http://stackoverflow.com/a/19542614/2257172">here</a>
* @throws GeneralSecurityException if a security error occurs
*/
public void disableCertificateValidation() throws GeneralSecurityException {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
@Override
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
@SuppressFBWarnings(value = "WEAK_TRUST_MANAGER")
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = (hostname, session) -> true;
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
}
/**
* Stops Remote control after testing requests.
*/
@After
public void tearDown() {
RemoteControl.stop();
}
/**
* Tests that sending an HTTP request without command results in HTTP 400, with all available commands in error message.
* @throws Exception if an error occurs
*/
@Test
public void testHttpListOfCommands() throws Exception {
testListOfCommands(httpBase);
}
/**
* Tests that sending an HTTPS request without command results in HTTP 400, with all available commands in error message.
* @throws Exception if an error occurs
*/
@Test
public void testHttpsListOfCommands() throws Exception {
testListOfCommands(httpsBase);
}
private void testListOfCommands(String url) throws IOException, ReflectiveOperationException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.connect();
assertEquals(connection.getResponseCode(), HttpURLConnection.HTTP_BAD_REQUEST);
try (InputStream is = connection.getErrorStream()) {
// TODO this code should be refactored somewhere in Utils as it is used in several JOSM classes
StringBuilder responseBody = new StringBuilder();
try (BufferedReader in = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String s;
while ((s = in.readLine()) != null) {
responseBody.append(s);
responseBody.append("\n");
}
}
assert responseBody.toString().contains(RequestProcessor.getUsageAsHtml());
}
}
}