package org.jboss.as.test.integration.web.handlers;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILE_HANDLER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LOGGER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PERIODIC_ROTATING_FILE_HANDLER;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROTOCOL;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.test.integration.management.util.ModelUtil.createOpNode;
import static org.jboss.as.test.shared.integration.ejb.security.PermissionUtils.createPermissionsXmlAsset;
import java.io.File;
import java.net.SocketPermission;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.PropertyPermission;
import org.apache.commons.io.FileUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.as.arquillian.api.ServerSetup;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.as.arquillian.container.ManagementClient;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.integration.management.ManagementOperations;
import org.jboss.as.test.integration.security.common.SecurityTestConstants;
import org.jboss.as.test.integration.security.common.Utils;
import org.jboss.as.test.integration.web.websocket.WebSocketTestCase;
import org.jboss.as.test.shared.ServerReload;
import org.jboss.as.test.shared.TestSuiteEnvironment;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests the use of undertow request dumping handler.
*
* @author <a href="mailto:jstourac@redhat.com">Jan Stourac</a>
*/
@RunWith(Arquillian.class)
@RunAsClient
@ServerSetup(RequestDumpingHandlerTestCase.RequestDumpingHandlerTestCaseSetupAction.class)
public class RequestDumpingHandlerTestCase {
public static class RequestDumpingHandlerTestCaseSetupAction implements ServerSetupTask {
private static String relativeTo;
private static ModelNode originalValue;
/** Name of the log file that will be used for testing. */
private static final String LOG_FILE_PREFIX = "test_server_" + System.currentTimeMillis();
private static final String LOG_FILE_SUFFIX = ".log";
// Address to server log setting
private static final PathAddress aLogAddr = PathAddress.pathAddress().append(SUBSYSTEM, "logging")
.append(PERIODIC_ROTATING_FILE_HANDLER, "FILE");
// Address to custom file handler
private static final String FILE_HANDLER_NAME = "testing-req-dump-handler";
private static final PathAddress ADDR_FILE_HANDLER = PathAddress.pathAddress().append(SUBSYSTEM, "logging")
.append(FILE_HANDLER, FILE_HANDLER_NAME);
// Address to custom logger
private static final String LOGGER_NAME = "io.undertow.request.dump";
private static final PathAddress ADDR_LOGGER = PathAddress.pathAddress().append(SUBSYSTEM, "logging")
.append(LOGGER, LOGGER_NAME);
private static final File WORK_DIR = new File("https-workdir");
public static final File SERVER_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.SERVER_KEYSTORE);
public static final File SERVER_TRUSTSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.SERVER_TRUSTSTORE);
public static final File CLIENT_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.CLIENT_KEYSTORE);
public static final File CLIENT_TRUSTSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.CLIENT_TRUSTSTORE);
public static final File UNTRUSTED_KEYSTORE_FILE = new File(WORK_DIR, SecurityTestConstants.UNTRUSTED_KEYSTORE);
private static final String HTTPS = "https";
private static final String HTTPS_LISTENER_PATH = "subsystem=undertow/server=default-server/https-listener=" + HTTPS;
private static final String HTTPS_REALM = "httpsRealm";
private static final String HTTPS_REALM_PATH = "core-service=management/security-realm=" + HTTPS_REALM;
private static final String HTTPS_REALM_AUTH_PATH = HTTPS_REALM_PATH + "/authentication=truststore";
private static final String HTTPS_REALM_SSL_PATH = HTTPS_REALM_PATH + "/server-identity=ssl";
@Override
public void setup(ManagementClient managementClient, String containerId) throws Exception {
// Retrieve original path to server log files
ModelNode op = Util.getReadAttributeOperation(aLogAddr, "file");
originalValue = ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
log.debug("Original value: " + originalValue.toString());
// Retrieve relative path to log files
relativeTo = originalValue.get("relative-to").asString();
op = Util.getReadAttributeOperation(PathAddress.pathAddress("path", relativeTo), "path");
ModelNode logPathModel = ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
logFilePath = Paths.get(logPathModel.asString() + File.separator + LOG_FILE_PREFIX + LOG_FILE_SUFFIX);
// Set custom file handler to log dumping requests into separate log file
ModelNode file = new ModelNode();
file.get("relative-to").set(relativeTo);
file.get("path").set(LOG_FILE_PREFIX + LOG_FILE_SUFFIX);
op = Util.createAddOperation(ADDR_FILE_HANDLER);
op.get(FILE).set(file);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
// Set custom logger that uses previous custom file handler for logging
op = Util.createAddOperation(ADDR_LOGGER);
LinkedList<ModelNode> handlers = new LinkedList<ModelNode>();
handlers.add(new ModelNode(FILE_HANDLER_NAME));
op.get("handlers").set(handlers);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
// Set HTTPS listener...
FileUtils.deleteDirectory(WORK_DIR);
WORK_DIR.mkdirs();
Utils.createKeyMaterial(WORK_DIR);
// add new HTTPS_REALM with SSL
ModelNode operation = createOpNode(HTTPS_REALM_PATH, ModelDescriptionConstants.ADD);
Utils.applyUpdate(operation, managementClient.getControllerClient());
operation = createOpNode(HTTPS_REALM_AUTH_PATH, ModelDescriptionConstants.ADD);
operation.get("keystore-path").set(SERVER_TRUSTSTORE_FILE.getAbsolutePath());
operation.get("keystore-password").set(SecurityTestConstants.KEYSTORE_PASSWORD);
Utils.applyUpdate(operation, managementClient.getControllerClient());
operation = createOpNode(HTTPS_REALM_SSL_PATH, ModelDescriptionConstants.ADD);
operation.get(PROTOCOL).set("TLSv1");
operation.get("keystore-path").set(SERVER_KEYSTORE_FILE.getAbsolutePath());
operation.get("keystore-password").set(SecurityTestConstants.KEYSTORE_PASSWORD);
Utils.applyUpdate(operation, managementClient.getControllerClient());
operation = createOpNode(HTTPS_LISTENER_PATH, ModelDescriptionConstants.ADD);
operation.get("socket-binding").set(HTTPS);
operation.get("security-realm").set(HTTPS_REALM);
Utils.applyUpdate(operation, managementClient.getControllerClient());
ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient());
}
@Override
public void tearDown(ManagementClient managementClient, String containerId) throws Exception {
// Remove custom logger and file-handler
ModelNode op = Util.createRemoveOperation(ADDR_LOGGER);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
op = Util.createRemoveOperation(ADDR_FILE_HANDLER);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
// Delete custom server log file
Files.delete(logFilePath);
// Delete folder with HTTPS files
FileUtils.deleteDirectory(WORK_DIR);
// Delete HTTPS specific configuration
op = createOpNode(HTTPS_REALM_SSL_PATH, ModelDescriptionConstants.REMOVE);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
op = createOpNode(HTTPS_REALM_AUTH_PATH, ModelDescriptionConstants.REMOVE);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
//ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient());
op = createOpNode(HTTPS_LISTENER_PATH, ModelDescriptionConstants.REMOVE);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
//ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient());
op = createOpNode(HTTPS_REALM_PATH, ModelDescriptionConstants.REMOVE);
ManagementOperations.executeOperation(managementClient.getControllerClient(), op);
ServerReload.executeReloadAndWaitForCompletion(managementClient.getControllerClient());
}
}
private static Logger log = Logger.getLogger(RequestDumpingHandlerTestCase.class);
/** Path to custom server log file. */
private static Path logFilePath;
private final String HTTPS_PORT = "8443";
private static final String DEPLOYMENT = "no-req-dump";
private static final String DEPLOYMENT_DUMP = "req-dump";
private static final String DEPLOYMENT_WS = "req-dump-ws";
@Deployment(name = DEPLOYMENT_DUMP)
public static WebArchive deployWithReqDump() {
WebArchive war = ShrinkWrap
.create(WebArchive.class, DEPLOYMENT_DUMP + ".war")
.addPackage(RequestDumpingHandlerTestCase.class.getPackage())
.addAsWebInfResource(RequestDumpingHandlerTestCase.class.getPackage(), "jboss-web-req-dump.xml",
"jboss-web.xml").addAsWebResource(new StringAsset("A file"), "file.txt");
return war;
}
@Deployment(name = DEPLOYMENT)
public static WebArchive deployWithoutReqDump() {
WebArchive war = ShrinkWrap.create(WebArchive.class, DEPLOYMENT + ".war")
.addPackage(RequestDumpingHandlerTestCase.class.getPackage())
.addAsWebInfResource(RequestDumpingHandlerTestCase.class.getPackage(), "jboss-web.xml", "jboss-web.xml")
.addAsWebResource(new StringAsset("A file"), "file.txt");
return war;
}
@Deployment(name = DEPLOYMENT_WS)
public static WebArchive deploy() {
WebArchive war = ShrinkWrap
.create(WebArchive.class, DEPLOYMENT_WS + ".war")
.addPackage(WebSocketTestCase.class.getPackage())
.addClass(TestSuiteEnvironment.class)
.addAsManifestResource(createPermissionsXmlAsset(
// Needed for the TestSuiteEnvironment.getServerAddress()
new PropertyPermission("management.address", "read"),
new PropertyPermission("node0", "read"),
new PropertyPermission("jboss.http.port", "read"),
// Needed for the serverContainer.connectToServer()
new SocketPermission("*:" + TestSuiteEnvironment.getHttpPort(), "connect,resolve")), "permissions.xml")
.addAsManifestResource(new StringAsset("io.undertow.websockets.jsr.UndertowContainerProvider"),
"services/javax.websocket.ContainerProvider")
.addAsWebInfResource(RequestDumpingHandlerTestCase.class.getPackage(), "jboss-web-req-dump.xml",
"jboss-web.xml");
return war;
}
/**
* Testing app has already defined request dumper handler. This test checks that when a request to URL is performed then
* request detail data is stored in proper format in the proper log file.
*/
@Test
@OperateOnDeployment(DEPLOYMENT_DUMP)
public void testReqDumpHandlerOn(@ArquillianResource URL url) throws Exception {
new RequestDumpingHandlerTestImpl.HttpRequestDumpingHandlerTestImpl(url.toURI(), logFilePath, true);
}
/**
* Testing app has no request dumper handler registered. This test checks that there is not dumped additional info about
* executed request in the log file.
*/
@Test
@OperateOnDeployment(DEPLOYMENT)
public void testReqDumpHandlerOff(@ArquillianResource URL url) throws Exception {
new RequestDumpingHandlerTestImpl.HttpRequestDumpingHandlerTestImpl(url.toURI(), logFilePath, false);
}
/**
* Testing app has request dumper handler registered. This test checks that request dump over the HTTPS is generated
* correctly.
*/
@Test
@OperateOnDeployment(DEPLOYMENT_DUMP)
public void testReqDumpHandlerOnHttps(@ArquillianResource URL url) throws Exception {
URL httpsUrl = new URL("https://" + url.getHost() + ":" + HTTPS_PORT + url.getPath() + "file.txt");
new RequestDumpingHandlerTestImpl.HttpsRequestDumpingHandlerTestImpl(httpsUrl.toURI(), logFilePath, true);
}
/**
* Testing app has request dumper handler registered. This test checks that request dump over the Websockets is generated
* correctly.
*/
@Test
@OperateOnDeployment(DEPLOYMENT_WS)
public void testReqDumpHandlerOnWebsockets(@ArquillianResource URL url) throws Exception {
URI wsUri = new URI("ws", "", TestSuiteEnvironment.getServerAddress(), TestSuiteEnvironment.getHttpPort(), "/" + DEPLOYMENT_WS + "/websocket/Stuart",
"", "");
new RequestDumpingHandlerTestImpl.WsRequestDumpingHandlerTestImpl(wsUri, logFilePath, true);
}
}