mirror of https://github.com/apache/nifi.git
NIFI-2529: Added support for SSLContextService protocols in HandleHttpRequest
Forced HandleHTTPRequest to use RestrictedSSLContextService and removed extraneous SSL algorithm checks Throw RuntimeException if the chosen SSL protocol isn't supported by HandleHttpRequest This closes #1985. Signed-off-by: Andy LoPresto <alopresto@apache.org>
This commit is contained in:
parent
2c1f5b49e4
commit
b7b6d9082c
|
@ -91,10 +91,11 @@ import com.sun.jersey.api.client.ClientResponse.Status;
|
|||
@WritesAttribute(attribute = "http.method", description = "The HTTP Method that was used for the request, such as GET or POST"),
|
||||
@WritesAttribute(attribute = HTTPUtils.HTTP_LOCAL_NAME, description = "IP address/hostname of the server"),
|
||||
@WritesAttribute(attribute = HTTPUtils.HTTP_PORT, description = "Listening port of the server"),
|
||||
@WritesAttribute(attribute = "http.query.string", description = "The query string portion of hte Request URL"),
|
||||
@WritesAttribute(attribute = "http.query.string", description = "The query string portion of the Request URL"),
|
||||
@WritesAttribute(attribute = HTTPUtils.HTTP_REMOTE_HOST, description = "The hostname of the requestor"),
|
||||
@WritesAttribute(attribute = "http.remote.addr", description = "The hostname:port combination of the requestor"),
|
||||
@WritesAttribute(attribute = "http.remote.user", description = "The username of the requestor"),
|
||||
@WritesAttribute(attribute = "http.protocol", description = "The protocol used to communicate"),
|
||||
@WritesAttribute(attribute = HTTPUtils.HTTP_REQUEST_URI, description = "The full Request URL"),
|
||||
@WritesAttribute(attribute = "http.auth.type", description = "The type of HTTP Authorization used"),
|
||||
@WritesAttribute(attribute = "http.principal.name", description = "The name of the authenticated user making the request"),
|
||||
|
@ -106,7 +107,7 @@ import com.sun.jersey.api.client.ClientResponse.Status;
|
|||
+ "attribute, prefixed with \"http.headers.\" For example, if the request contains an HTTP Header named \"x-my-header\", then the value "
|
||||
+ "will be added to an attribute named \"http.headers.x-my-header\"")})
|
||||
@SeeAlso(value = {HandleHttpResponse.class},
|
||||
classNames = {"org.apache.nifi.http.StandardHttpContextMap", "org.apache.nifi.ssl.StandardSSLContextService"})
|
||||
classNames = {"org.apache.nifi.http.StandardHttpContextMap", "org.apache.nifi.ssl.RestrictedStandardSSLContextService"})
|
||||
public class HandleHttpRequest extends AbstractProcessor {
|
||||
|
||||
private static final Pattern URL_QUERY_PARAM_DELIMITER = Pattern.compile("&");
|
||||
|
@ -448,6 +449,8 @@ public class HandleHttpRequest extends AbstractProcessor {
|
|||
sslFactory.setNeedClientAuth(needClientAuth);
|
||||
sslFactory.setWantClientAuth(wantClientAuth);
|
||||
|
||||
sslFactory.setProtocol(sslService.getSslAlgorithm());
|
||||
|
||||
if (sslService.isKeyStoreConfigured()) {
|
||||
sslFactory.setKeyStorePath(sslService.getKeyStoreFile());
|
||||
sslFactory.setKeyStorePassword(sslService.getKeyStorePassword());
|
||||
|
@ -528,6 +531,7 @@ public class HandleHttpRequest extends AbstractProcessor {
|
|||
putAttribute(attributes, HTTPUtils.HTTP_REMOTE_HOST, request.getRemoteHost());
|
||||
putAttribute(attributes, "http.remote.addr", request.getRemoteAddr());
|
||||
putAttribute(attributes, "http.remote.user", request.getRemoteUser());
|
||||
putAttribute(attributes, "http.protocol", request.getProtocol());
|
||||
putAttribute(attributes, HTTPUtils.HTTP_REQUEST_URI, request.getRequestURI());
|
||||
putAttribute(attributes, "http.request.url", request.getRequestURL().toString());
|
||||
putAttribute(attributes, "http.auth.type", request.getAuthType());
|
||||
|
|
|
@ -22,9 +22,13 @@ import java.io.IOException;
|
|||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -32,6 +36,9 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.apache.nifi.controller.AbstractControllerService;
|
||||
import org.apache.nifi.http.HttpContextMap;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
|
||||
import org.apache.nifi.ssl.StandardSSLContextService;
|
||||
import org.apache.nifi.stream.io.NullOutputStream;
|
||||
import org.apache.nifi.stream.io.StreamUtils;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
|
@ -42,6 +49,36 @@ import org.junit.Test;
|
|||
|
||||
public class TestHandleHttpRequest {
|
||||
|
||||
private static Map<String, String> getTruststoreProperties() {
|
||||
final Map<String, String> props = new HashMap<>();
|
||||
props.put(StandardSSLContextService.TRUSTSTORE.getName(), "src/test/resources/localhost-ts.jks");
|
||||
props.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), "localtest");
|
||||
props.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), "JKS");
|
||||
return props;
|
||||
}
|
||||
|
||||
private static Map<String, String> getKeystoreProperties() {
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/localhost-ks.jks");
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), "localtest");
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), "JKS");
|
||||
return properties;
|
||||
}
|
||||
|
||||
private static SSLContext useSSLContextService(final TestRunner controller, final Map<String, String> sslProperties) {
|
||||
final SSLContextService service = new StandardRestrictedSSLContextService();
|
||||
try {
|
||||
controller.addControllerService("ssl-service", service, sslProperties);
|
||||
controller.enableControllerService(service);
|
||||
} catch (InitializationException ex) {
|
||||
ex.printStackTrace();
|
||||
Assert.fail("Could not create SSL Context Service");
|
||||
}
|
||||
|
||||
controller.setProperty(HandleHttpRequest.SSL_CONTEXT, "ssl-service");
|
||||
return service.createSSLContext(SSLContextService.ClientAuth.WANT);
|
||||
}
|
||||
|
||||
@Test(timeout=10000)
|
||||
public void testRequestAddedToService() throws InitializationException, MalformedURLException, IOException, InterruptedException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(HandleHttpRequest.class);
|
||||
|
@ -165,6 +202,73 @@ public class TestHandleHttpRequest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecure() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(HandleHttpRequest.class);
|
||||
runner.setProperty(HandleHttpRequest.PORT, "0");
|
||||
|
||||
final MockHttpContextMap contextMap = new MockHttpContextMap();
|
||||
runner.addControllerService("http-context-map", contextMap);
|
||||
runner.enableControllerService(contextMap);
|
||||
runner.setProperty(HandleHttpRequest.HTTP_CONTEXT_MAP, "http-context-map");
|
||||
|
||||
final Map<String, String> sslProperties = getKeystoreProperties();
|
||||
sslProperties.putAll(getTruststoreProperties());
|
||||
sslProperties.put(StandardSSLContextService.SSL_ALGORITHM.getName(), "TLSv1.2");
|
||||
final SSLContext sslContext = useSSLContextService(runner, sslProperties);
|
||||
|
||||
// trigger processor to stop but not shutdown.
|
||||
runner.run(1, false);
|
||||
try {
|
||||
final Thread httpThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final int port = ((HandleHttpRequest) runner.getProcessor()).getPort();
|
||||
final HttpsURLConnection connection = (HttpsURLConnection) new URL("https://localhost:"
|
||||
+ port + "/my/path?query=true&value1=value1&value2=&value3&value4=apple=orange").openConnection();
|
||||
|
||||
connection.setSSLSocketFactory(sslContext.getSocketFactory());
|
||||
connection.setDoOutput(false);
|
||||
connection.setRequestMethod("GET");
|
||||
connection.setRequestProperty("header1", "value1");
|
||||
connection.setRequestProperty("header2", "");
|
||||
connection.setRequestProperty("header3", "apple=orange");
|
||||
connection.setConnectTimeout(3000);
|
||||
connection.setReadTimeout(3000);
|
||||
|
||||
StreamUtils.copy(connection.getInputStream(), new NullOutputStream());
|
||||
} catch (final Throwable t) {
|
||||
t.printStackTrace();
|
||||
Assert.fail(t.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
httpThread.start();
|
||||
|
||||
while ( runner.getFlowFilesForRelationship(HandleHttpRequest.REL_SUCCESS).isEmpty() ) {
|
||||
// process the request.
|
||||
runner.run(1, false, false);
|
||||
}
|
||||
|
||||
runner.assertAllFlowFilesTransferred(HandleHttpRequest.REL_SUCCESS, 1);
|
||||
assertEquals(1, contextMap.size());
|
||||
|
||||
final MockFlowFile mff = runner.getFlowFilesForRelationship(HandleHttpRequest.REL_SUCCESS).get(0);
|
||||
mff.assertAttributeEquals("http.query.param.query", "true");
|
||||
mff.assertAttributeEquals("http.query.param.value1", "value1");
|
||||
mff.assertAttributeEquals("http.query.param.value2", "");
|
||||
mff.assertAttributeEquals("http.query.param.value3", "");
|
||||
mff.assertAttributeEquals("http.query.param.value4", "apple=orange");
|
||||
mff.assertAttributeEquals("http.headers.header1", "value1");
|
||||
mff.assertAttributeEquals("http.headers.header3", "apple=orange");
|
||||
mff.assertAttributeEquals("http.protocol", "HTTP/1.1");
|
||||
} finally {
|
||||
// shut down the server
|
||||
runner.run(1, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockHttpContextMap extends AbstractControllerService implements HttpContextMap {
|
||||
|
||||
private boolean registerSuccessfully = true;
|
||||
|
|
Loading…
Reference in New Issue