/* * Copyright 2017 ThoughtWorks, 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 com.thoughtworks.go.server.web; import com.thoughtworks.go.agent.common.ssl.GoAgentServerHttpClientBuilder; import com.thoughtworks.go.domain.ServerSiteUrlConfig; import com.thoughtworks.go.server.util.HttpTestUtil; import com.thoughtworks.go.server.util.ServletHelper; import com.thoughtworks.go.util.SslVerificationMode; import com.thoughtworks.go.util.SystemEnvironment; import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.client.methods.*; import org.apache.http.impl.client.CloseableHttpClient; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.experimental.theories.DataPoint; import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theory; import org.junit.runner.RunWith; import org.springframework.web.context.WebApplicationContext; import org.tuckey.web.filters.urlrewrite.UrlRewriteFilter; import javax.servlet.DispatcherType; import java.io.File; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(Theories.class) public class UrlRewriterIntegrationTest { public static HttpTestUtil httpUtil; public static final int HTTP = 5197; public static final int HTTPS = 9071; public static WebApplicationContext wac; public static boolean useConfiguredUrls; public static String originalSslPort; @BeforeClass public static void beforeClass() throws Exception { ServletHelper.init(); httpUtil = new HttpTestUtil(new HttpTestUtil.ContextCustomizer() { public void customize(WebAppContext ctx) throws Exception { wac = mock(WebApplicationContext.class); ctx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); URL resource = getClass().getClassLoader().getResource("WEB-INF/urlrewrite.xml"); if (resource == null) { throw new RuntimeException("Cannot load WEB-INF/urlrewrite.xml"); } ctx.setBaseResource(Resource.newResource(new File(resource.getFile()).getParent())); ctx.addFilter(UrlRewriteFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)).setInitParameter("confPath", "/urlrewrite.xml"); ctx.addServlet(HttpTestUtil.EchoServlet.class, "/*"); } }); httpUtil.httpConnector(HTTP); httpUtil.httpsConnector(HTTPS); when(wac.getBean("serverConfigService")).thenReturn(new BaseUrlProvider() { public boolean hasAnyUrlConfigured() { return useConfiguredUrls; } public String siteUrlFor(String url, boolean forceSsl) throws URISyntaxException { ServerSiteUrlConfig siteUrl = forceSsl ? new ServerSiteUrlConfig("https://127.2.2.2:" + 9071) : new ServerSiteUrlConfig("http://127.2.2.2:" + 5197); return siteUrl.siteUrlFor(url); } }); httpUtil.start(); originalSslPort = System.getProperty(SystemEnvironment.CRUISE_SERVER_SSL_PORT); System.setProperty(SystemEnvironment.CRUISE_SERVER_SSL_PORT, String.valueOf(9071)); } @AfterClass public static void afterClass() { if (originalSslPort == null) { System.getProperties().remove(SystemEnvironment.CRUISE_SERVER_SSL_PORT); } else { System.setProperty(SystemEnvironment.CRUISE_SERVER_SSL_PORT, originalSslPort); } httpUtil.stop(); } enum METHOD { GET, POST, PUT } private static class ResponseAssertion { private final String requestedUrl; private final String servedUrl; private boolean useConfiguredUrls = false; private Map<String, String> responseHeaders = new HashMap<>(); private int responseCode = 200; private METHOD method; public ResponseAssertion(String requestedUrl, String servedUrl, METHOD method) { this.requestedUrl = requestedUrl; this.servedUrl = servedUrl; this.method = method; } public ResponseAssertion(String requestedUrl, String servedUrl) { this(requestedUrl, servedUrl, METHOD.GET); } public ResponseAssertion(String requestedUrl, String servedUrl, boolean useConfiguredUrls) { this(requestedUrl, servedUrl); this.useConfiguredUrls = useConfiguredUrls; } public ResponseAssertion(String requestedUrl, String servedUrl, METHOD method, boolean useConfiguredUrls) { this(requestedUrl, servedUrl, method); this.useConfiguredUrls = useConfiguredUrls; } } @DataPoint public static ResponseAssertion NO_REWRITE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/quux?hello=world", "http://127.1.1.1:" + HTTP + "/go/quux?hello=world"); @DataPoint public static ResponseAssertion NO_REWRITE_SSL = new ResponseAssertion("https://127.1.1.1:" + HTTPS + "/go/quux?hello=world", "https://127.1.1.1:" + HTTPS + "/go/quux?hello=world"); @DataPoint public static ResponseAssertion OAUTH = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/foo/oauth/bar?hello=world", "http://127.1.1.1:" + HTTP + "/go/foo/oauth/bar?hello=world");//error handled in ssh_helper @DataPoint public static ResponseAssertion RAILS_BOUND = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/agents/foo?hello=world", "http://127.1.1.1:" + HTTP + "/go/rails/agents/foo?hello=world"); @DataPoint public static ResponseAssertion RAILS_BOUND_SSL = new ResponseAssertion("https://127.1.1.1:" + HTTPS + "/go/agents/foo?hello=world", "https://127.1.1.1:" + HTTPS + "/go/rails/agents/foo?hello=world"); @DataPoint public static ResponseAssertion PIPELINE_GROUPS_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/pipelines?foo=bar&baz=quux", "http://127.1.1.1:" + HTTP + "/go/rails/admin/pipelines?foo=bar&baz=quux", true); @DataPoint public static ResponseAssertion PIPELINE_GROUP_EDIT = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/pipeline_group/group.name?foo=bar&baz=quux", "http://127.1.1.1:" + HTTP + "/go/rails/admin/pipeline_group/group.name?foo=bar&baz=quux", true); @DataPoint public static ResponseAssertion PIPELINE_GROUP_CREATE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/pipeline_group", "http://127.1.1.1:" + HTTP + "/go/rails/admin/pipeline_group", true); @DataPoint public static ResponseAssertion TEMPLATES_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/templates?foo=bar&baz=quux", "http://127.1.1.1:" + HTTP + "/go/rails/admin/templates?foo=bar&baz=quux", true); @DataPoint public static ResponseAssertion CONFIG_VIEW = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/config_view/templates/template_name", "http://127.1.1.1:" + HTTP + "/go/rails/config_view/templates/template_name"); @DataPoint public static ResponseAssertion PIPELINE_NEW = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/pipeline/new", "http://127.1.1.1:" + HTTP + "/go/rails/admin/pipeline/new", true); @DataPoint public static ResponseAssertion PIPELINE_CREATE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/pipelines", "http://127.1.1.1:" + HTTP + "/go/rails/admin/pipelines", METHOD.POST); @DataPoint public static ResponseAssertion SERVER_BACKUP = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/backup", "http://127.1.1.1:" + HTTP + "/go/rails/admin/backup", true); @DataPoint public static ResponseAssertion SERVER_BACKUP_OTHER_ACTIONS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/backup/foo?abc=dfx", "http://127.1.1.1:" + HTTP + "/go/rails/admin/backup/foo?abc=dfx", true); @DataPoint public static ResponseAssertion STATIC_PAGES = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/static/foo.html?bar=baz", "http://127.1.1.1:" + HTTP + "/go/static/foo.html?bar=baz", true); @DataPoint public static final ResponseAssertion CONFIG_FILE_XML = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/configuration/file.xml", "http://127.1.1.1:" + HTTP + "/go/admin/restful/configuration/file/GET/xml"); @DataPoint public static final ResponseAssertion CONFIG_API_FOR_CURRENT = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/api/admin/config.xml", "http://127.1.1.1:" + HTTP + "/go/admin/restful/configuration/file/GET/xml?version=current"); @DataPoint public static final ResponseAssertion CONFIG_API_FOR_HISTORICAL = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/api/admin/config/some-md5.xml", "http://127.1.1.1:" + HTTP + "/go/admin/restful/configuration/file/GET/historical-xml?version=some-md5"); @DataPoint public static ResponseAssertion IMAGES_WHILE_BACKUP_IS_IN_PROGRESS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/images/foo.png", "http://127.1.1.1:" + HTTP + "/go/images/foo.png"); @DataPoint public static ResponseAssertion JAVASCRIPT_WHILE_BACKUP_IS_IN_PROGRESS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/javascripts/foo.js", "http://127.1.1.1:" + HTTP + "/go/javascripts/foo.js"); @DataPoint public static ResponseAssertion COMPRESSED_JAVASCRIPT_WHILE_BACKUP_IS_IN_PROGRESS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/compressed/all.js", "http://127.1.1.1:" + HTTP + "/go/compressed/all.js"); @DataPoint public static ResponseAssertion STYLESHEETS_WHILE_BACKUP_IS_IN_PROGRESS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/stylesheets/foo.css", "http://127.1.1.1:" + HTTP + "/go/stylesheets/foo.css", true); @DataPoint public static ResponseAssertion TASKS_LOOKUP_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/commands", "http://127.1.1.1:" + HTTP + "/go/rails/admin/commands", true); @DataPoint public static ResponseAssertion TASKS_LOOKUP_SHOW = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/commands/show", "http://127.1.1.1:" + HTTP + "/go/rails/admin/commands/show", true); @DataPoint public static ResponseAssertion PLUGINS_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/plugins", "http://127.1.1.1:" + HTTP + "/go/rails/admin/plugins", true); @DataPoint public static ResponseAssertion PACKAGE_REPOSITORIES_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/package_repositories", "http://127.1.1.1:" + HTTP + "/go/rails/admin/package_repositories", true); @DataPoint public static ResponseAssertion PACKAGE_DEFINITIONS = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/package_definitions", "http://127.1.1.1:" + HTTP + "/go/rails/admin/package_definitions", true); @DataPoint public static ResponseAssertion PLUGGABLE_SCM = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/materials/pluggable_scm/check_connection/plugin_id", "http://127.1.1.1:" + HTTP + "/go/rails/admin/materials/pluggable_scm/check_connection/plugin_id", true); @DataPoint public static ResponseAssertion CONFIG_CHANGE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/config_change/md5_value", "http://127.1.1.1:" + HTTP + "/go/rails/config_change/md5_value", true); @DataPoint public static ResponseAssertion CONFIG_XML_VIEW = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/config_xml", "http://127.1.1.1:" + HTTP + "/go/rails/admin/config_xml", METHOD.GET, true); @DataPoint public static ResponseAssertion CONFIG_XML_EDIT = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/config_xml/edit", "http://127.1.1.1:" + HTTP + "/go/rails/admin/config_xml/edit", METHOD.GET, true); @DataPoint public static ResponseAssertion ARTIFACT_API_HTML_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/job.html", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/GET/html?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=job&filePath=", true); @DataPoint public static ResponseAssertion ARTIFACT_API_HTML_LISTING_FILENAME = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/target/abc%2Bfoo.txt", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/GET/?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=target&filePath=abc%2Bfoo.txt", true); @DataPoint public static ResponseAssertion ARTIFACT_API_JSON_LISTING = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/job.json", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/GET/json?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=job&filePath=", true); @DataPoint public static ResponseAssertion ARTIFACT_API_GET_FILE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/job//tmp/file", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/GET/?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=job&filePath=%2Ftmp%2Ffile", true); @DataPoint public static ResponseAssertion ARTIFACT_API_PUSH_FILE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/job//tmp/file", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/POST/?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=job&filePath=%2Ftmp%2Ffile", METHOD.POST, true); @DataPoint public static ResponseAssertion ARTIFACT_API_CHANGE_FILE = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/files/pipeline/1/stage/1/job/file", "http://127.1.1.1:" + HTTP + "/go/repository/restful/artifact/PUT/?pipelineName=pipeline&pipelineLabel=1&stageName=stage&stageCounter=1&buildName=job&filePath=file", METHOD.PUT, true); @DataPoint public static ResponseAssertion ADMIN_GARAGE_INDEX = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/garage", "http://127.1.1.1:" + HTTP + "/go/rails/admin/garage"); @DataPoint public static ResponseAssertion ADMIN_GARAGE_GC = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/admin/garage/gc", "http://127.1.1.1:" + HTTP + "/go/rails/admin/garage/gc", METHOD.POST); @DataPoint public static ResponseAssertion PIPELINE_DASHBOARD_JSON = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/pipelines.json", "http://127.1.1.1:" + HTTP + "/go/rails/pipelines.json", METHOD.GET); @DataPoint public static ResponseAssertion MATERIALS_VALUE_STREAM_MAP = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/materials/value_stream_map/fingerprint/revision", "http://127.1.1.1:" + HTTP + "/go/rails/materials/value_stream_map/fingerprint/revision", METHOD.GET); @DataPoint public static ResponseAssertion RAILS_INTERNAL_API = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/api/config/internal/pluggable_task/indix.s3fetch", "http://127.1.1.1:" + HTTP + "/go/rails/api/config/internal/pluggable_task/indix.s3fetch", METHOD.GET); @DataPoint public static ResponseAssertion USERS_INDEX_API = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/api/users", "http://127.1.1.1:" + HTTP + "/go/rails/api/users", METHOD.GET); @DataPoint public static ResponseAssertion USERS_SHOW_API = new ResponseAssertion("http://127.1.1.1:" + HTTP + "/go/api/users/some.one", "http://127.1.1.1:" + HTTP + "/go/rails/api/users/some.one", METHOD.GET); @Theory public void shouldRewrite(final ResponseAssertion assertion) throws Exception { useConfiguredUrls = assertion.useConfiguredUrls; GoAgentServerHttpClientBuilder builder = new GoAgentServerHttpClientBuilder(null, SslVerificationMode.NONE); try (CloseableHttpClient httpClient = builder.build()) { HttpRequestBase httpMethod; if (assertion.method == METHOD.GET) { httpMethod = new HttpGet(assertion.requestedUrl); } else if (assertion.method == METHOD.POST) { httpMethod = new HttpPost(assertion.requestedUrl); } else if (assertion.method == METHOD.PUT) { httpMethod = new HttpPut(assertion.requestedUrl); } else { throw new RuntimeException("Method has to be one of GET, POST and PUT. Was: " + assertion.method); } try (CloseableHttpResponse response = httpClient.execute(httpMethod)) { assertThat("status code match failed", response.getStatusLine().getStatusCode(), is(assertion.responseCode)); assertThat("handler url match failed", IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8), is(assertion.servedUrl)); for (Map.Entry<String, String> headerValPair : assertion.responseHeaders.entrySet()) { Header responseHeader = response.getFirstHeader(headerValPair.getKey()); if (headerValPair.getValue() == null) { assertThat("header match failed", responseHeader, is(nullValue())); } else { assertThat("header match failed", responseHeader.getValue(), is(headerValPair.getValue())); } } } } } }