/*
Copyright 2016 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.jboss.as.test.integration.management.extension.customcontext.testbase;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.EXTENSION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OPERATION_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ROLLBACK_ON_RUNTIME_FAILURE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.test.http.Authentication;
import org.jboss.as.test.integration.management.extension.EmptySubsystemParser;
import org.jboss.as.test.integration.management.extension.ExtensionUtils;
import org.jboss.as.test.integration.management.extension.customcontext.CustomContextExtension;
import org.jboss.dmr.ModelNode;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.wildfly.core.testrunner.ManagementClient;
/**
* Base class for tests of integrating a custom management context on the http interface.
* We install an extension that does this kind of integration and see the result.
*
* @author Brian Stansberry
*/
public abstract class CustomManagementContextTestBase {
protected static final PathAddress EXT = PathAddress.pathAddress(EXTENSION, CustomContextExtension.EXTENSION_NAME);
protected static final PathElement SUB = PathElement.pathElement(SUBSYSTEM, CustomContextExtension.SUBSYSTEM_NAME);
@BeforeClass
public static void setup() throws IOException {
ExtensionUtils.createExtensionModule(CustomContextExtension.EXTENSION_NAME, CustomContextExtension.class,
EmptySubsystemParser.class.getPackage());
}
@After
public void teardown() throws IOException {
IOException ioe = null;
AssertionError ae = null;
RuntimeException re = null;
ManagementClient managementClient = getManagementClient();
PathAddress[] cleanUp = {getSubsystemAddress(), getExtensionAddress()};
for (int i = 0; i < cleanUp.length; i++) {
try {
ModelNode op = Util.createRemoveOperation(cleanUp[i]);
op.get(OPERATION_HEADERS, ROLLBACK_ON_RUNTIME_FAILURE).set(false);
executeOp(op, managementClient);
} catch (IOException e) {
if (ioe == null) {
ioe = e;
}
} catch (AssertionError e) {
if (i > 0 && ae == null) {
ae = e;
} // else ignore because in a failed test SUB may not exist, causing remove to fail
} catch (RuntimeException e) {
if (re == null) {
re = e;
}
}
}
if (ioe != null) {
throw ioe;
}
if (ae != null) {
throw ae;
}
if (re != null) {
throw re;
}
}
@AfterClass
public static void cleanupExtension() {
//if (true) return;
ExtensionUtils.deleteExtensionModule(CustomContextExtension.EXTENSION_NAME);
}
protected abstract PathAddress getExtensionAddress();
protected abstract PathAddress getSubsystemAddress();
protected abstract ManagementClient getManagementClient();
@Test
public void test() throws IOException {
test(getManagementClient());
}
private void test(final ManagementClient managementClient) throws IOException {
//if (true) return;
final String urlBase = "http://" + managementClient.getMgmtAddress() + ":9990/";
final String remapUrl = urlBase + "remap/foo";
final String badRemapUrl = urlBase + "remap/bad";
final String staticUrl = urlBase + "static/hello.txt";
final String staticUrlDirectory = urlBase + "static/";
final String badStaticUrl = urlBase + "static/bad.txt";
// Sanity check
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpResponse resp = client.execute(new HttpGet(remapUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
resp = client.execute(new HttpGet(staticUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
}
// Add extension and subsystem
executeOp(Util.createAddOperation(getExtensionAddress()), managementClient);
executeOp(Util.createAddOperation(getSubsystemAddress()), managementClient);
// Unauthenticated check
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpResponse resp = client.execute(new HttpGet(remapUrl));
assertEquals(401, resp.getStatusLine().getStatusCode());
resp = client.execute(new HttpGet(staticUrl));
assertEquals(200, resp.getStatusLine().getStatusCode());
}
try (CloseableHttpClient client = createAuthenticatingClient(managementClient)) {
// Authenticated check
HttpResponse resp = client.execute(new HttpGet(remapUrl));
assertEquals(200, resp.getStatusLine().getStatusCode());
ModelNode respNode = ModelNode.fromJSONString(EntityUtils.toString(resp.getEntity()));
assertEquals(respNode.toString(), CustomContextExtension.EXTENSION_NAME, respNode.get("module").asString());
assertTrue(respNode.toString(), respNode.hasDefined("subsystem"));
assertTrue(respNode.toString(), respNode.get("subsystem").has(CustomContextExtension.SUBSYSTEM_NAME));
resp = client.execute(new HttpGet(staticUrl));
assertEquals(200, resp.getStatusLine().getStatusCode());
String text = EntityUtils.toString(resp.getEntity());
assertTrue(text, text.startsWith("Hello"));
// the response should contain headers:
// X-Frame-Options: SAMEORIGIN
// Cache-Control: public, max-age=2678400
final Map<String, String> headersMap = Arrays.stream(resp.getAllHeaders())
.collect(Collectors.toMap(Header::getName, Header::getValue));
Assert.assertTrue("'X-Frame-Options: SAMEORIGIN' header is expected",
headersMap.getOrDefault("X-Frame-Options", "").contains("SAMEORIGIN"));
Assert.assertTrue("Cache-Control header with max-age=2678400 is expected,",
headersMap.getOrDefault("Cache-Control", "").contains("max-age=2678400"));
// directory listing is not allowed
resp = client.execute(new HttpGet(staticUrlDirectory));
assertEquals(404, resp.getStatusLine().getStatusCode());
// POST check
resp = client.execute(new HttpPost(remapUrl));
assertEquals(405, resp.getStatusLine().getStatusCode());
resp = client.execute(new HttpPost(staticUrl));
assertEquals(200, resp.getStatusLine().getStatusCode());
// Bad URL check
resp = client.execute(new HttpGet(badRemapUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
resp = client.execute(new HttpGet(badStaticUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
// Remove subsystem
executeOp(Util.createRemoveOperation(getSubsystemAddress()), managementClient);
// Requests fail
resp = client.execute(new HttpGet(remapUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
resp = client.execute(new HttpGet(staticUrl));
assertEquals(404, resp.getStatusLine().getStatusCode());
}
}
private static CloseableHttpClient createAuthenticatingClient(ManagementClient managementClient) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(managementClient.getMgmtAddress(), 9990),
new UsernamePasswordCredentials(Authentication.USERNAME, Authentication.PASSWORD));
return HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.setMaxConnPerRoute(10)
.build();
}
private static ModelNode executeOp(ModelNode op, ManagementClient managementClient) throws IOException {
ModelNode response = managementClient.getControllerClient().execute(op);
assertTrue(response.toString(), response.hasDefined(OUTCOME));
assertEquals(response.toString(), SUCCESS, response.get(OUTCOME).asString());
return response;
}
}