package org.geowebcache.jetty; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.describedAs; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasProperty; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import java.io.InputStream; import java.net.URI; import java.util.Arrays; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.custommonkey.xmlunit.XMLUnit; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.springframework.util.xml.SimpleNamespaceContext; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; /** * Integration test for the REST API in a full GWC instance * * @author Kevin Smith, Boundless * */ public class RestIT { @ClassRule static public JettyRule jetty = new JettyRule(); @Rule public HttpClientRule anonymous = HttpClientRule.anonymous(); @Rule public HttpClientRule admin = new HttpClientRule("geowebcache", "secured", "admin"); @Rule public HttpClientRule badPassword = new HttpClientRule("geowebcache", "notTheRightPassword", "badPassword"); @Rule public HttpClientRule notAUser = new HttpClientRule("notARealUser", "somePassword", "notAUser"); private SimpleNamespaceContext nsContext; @Before public void setUp() { nsContext = new SimpleNamespaceContext(); nsContext.bindNamespaceUri("atom", "http://www.w3.org/2005/Atom"); nsContext.bindNamespaceUri("wmts", "http://www.opengis.net/wmts/1.0"); nsContext.bindNamespaceUri("ows", "http://www.opengis.net/ows/1.1"); } Matcher<Node> hasXPath(final String xpathExpr, final Matcher<String> matcher) { return Matchers.hasXPath(xpathExpr, nsContext, matcher); } Matcher<Node> hasXPath(final String xpathExpr) { return Matchers.hasXPath(xpathExpr, nsContext); } @Test public void testGetLayers() throws Exception { doGetXML( "rest/layers.xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/layers/layer[name/text()='img states']/atom:link/@href", equalTo(jetty.getUri().resolve("rest/layers/img+states.xml").toString()))); assertThat(doc, hasXPath("/layers/layer[name/text()='topp:states']/atom:link/@href", equalTo(jetty.getUri().resolve("rest/layers/topp%3Astates.xml").toString()))); assertThat(doc, hasXPath("/layers/layer[name/text()='raster test layer']/atom:link/@href", equalTo(jetty.getUri().resolve("rest/layers/raster+test+layer.xml").toString()))); assertThat(doc, hasXPath("count(/layers/layer)", equalTo("3"))); }); } @Test public void testCreateUpdateDelete() throws Exception { final String layerName = "testLayer"; final String url1 = "http://example.com/wms1?"; final String url2 = "http://example.com/wms2?"; final String layers = "remoteLayer"; // Create { final HttpPut request = new HttpPut(jetty.getUri().resolve("rest/layers/").resolve(layerName+".xml")); request.setEntity(new StringEntity("<wmsLayer><name>"+layerName+"</name><wmsUrl><string>"+url1+"</string></wmsUrl><wmsLayers>"+layers+"</wmsLayers></wmsLayer>", ContentType.APPLICATION_XML)); try( CloseableHttpResponse response = admin.getClient().execute(request)) { assertThat(response.getStatusLine(), hasProperty("statusCode", equalTo(200))); } doGetXML( "rest/layers.xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/layers/layer[name/text()='"+layerName+"']/atom:link/@href", equalTo(jetty.getUri().resolve("rest/layers/"+layerName+".xml").toString()))); }); doGetXML( "rest/layers/"+layerName+".xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/wmsLayer/name", equalTo(layerName))); assertThat(doc, hasXPath("/wmsLayer/wmsUrl/string", equalTo(url1))); assertThat(doc, hasXPath("/wmsLayer/wmsLayers", equalTo(layers))); }); } // Update { final HttpPost request = new HttpPost(jetty.getUri().resolve("rest/layers/").resolve(layerName+".xml")); request.setEntity(new StringEntity("<wmsLayer><name>"+layerName+"</name><wmsUrl><string>"+url2+"</string></wmsUrl><wmsLayers>"+layers+"</wmsLayers></wmsLayer>", ContentType.APPLICATION_XML)); try( CloseableHttpResponse response = admin.getClient().execute(request)) { assertThat(response.getStatusLine(), hasProperty("statusCode", equalTo(200))); } doGetXML( "rest/layers/"+layerName+".xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/wmsLayer/name", equalTo(layerName))); assertThat(doc, hasXPath("/wmsLayer/wmsUrl/string", equalTo(url2))); assertThat(doc, hasXPath("/wmsLayer/wmsLayers", equalTo(layers))); }); } // GetCap { doGetXML("service/wmts?REQUEST=getcapabilities", anonymous.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/wmts:Capabilities/wmts:Contents/wmts:Layer/ows:Title[text()='"+layerName+"']")); }); } // Delete { final HttpDelete request = new HttpDelete(jetty.getUri().resolve("rest/layers/").resolve(layerName+".xml")); try( CloseableHttpResponse response = admin.getClient().execute(request)) { assertThat(response.getStatusLine(), hasProperty("statusCode", equalTo(200))); } doGetXML( "rest/layers.xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, not(hasXPath("/layers/layer[name/text()='"+layerName+"']"))); }); final HttpGet request2 = new HttpGet(jetty.getUri().resolve("rest/layers/").resolve(layerName+".xml")); try( CloseableHttpResponse response = admin.getClient().execute(request2)) { assertThat(response.getStatusLine(), hasProperty("statusCode", equalTo(404))); } } // GetCap { doGetXML("service/wmts?REQUEST=getcapabilities", anonymous.getClient(), equalTo(200), doc->{ assertThat(doc, not(hasXPath("/wmts:Capabilities/wmts:Contents/wmts:Layer/ows:Title[text()='"+layerName+"']"))); }); } } @Test public void testInvalidMethods() throws Exception { // Check that all permutations of method and user produce the expected status code. for(HttpUriRequest request: Arrays.asList( new HttpDelete(jetty.getUri().resolve("rest/layers.xml")), new HttpPost(jetty.getUri().resolve("rest/layers.xml")), new HttpPut(jetty.getUri().resolve("rest/layers.xml")), new HttpPut(jetty.getUri().resolve("rest/seed/img+states.xml")), new HttpDelete(jetty.getUri().resolve("rest/seed/img+states.xml")), new HttpDelete(jetty.getUri().resolve("rest/seed")), new HttpPut(jetty.getUri().resolve("rest/seed")), new HttpDelete(jetty.getUri().resolve("rest/diskquota.xml")), new HttpDelete(jetty.getUri().resolve("rest/diskquota.json")), new HttpDelete(jetty.getUri().resolve("rest/diskquota")), new HttpPut(jetty.getUri().resolve("rest/masstruncate")), new HttpDelete(jetty.getUri().resolve("rest/masstruncate")), new HttpPut(jetty.getUri().resolve("rest/statistics")), new HttpPost(jetty.getUri().resolve("rest/statistics")), new HttpDelete(jetty.getUri().resolve("rest/statistics")), new HttpPut(jetty.getUri().resolve("rest/reload")), new HttpGet(jetty.getUri().resolve("rest/reload")), new HttpDelete(jetty.getUri().resolve("rest/reload")) )) { testSecured(request, equalTo(405)); } } @Test public void testSecure() throws Exception { for(HttpUriRequest request: Arrays.asList( new HttpGet(jetty.getUri().resolve("rest/layers.xml")), new HttpGet(jetty.getUri().resolve("rest/seed/img+states")), new HttpPost(jetty.getUri().resolve("rest/reload")), new HttpPost(jetty.getUri().resolve("rest/seed/img+states.xml")), new HttpGet(jetty.getUri().resolve("rest/seed/img+states.xml")), new HttpGet(jetty.getUri().resolve("rest/seed")), new HttpPost(jetty.getUri().resolve("rest/seed")), new HttpGet(jetty.getUri().resolve("rest/masstruncate")), new HttpPost(jetty.getUri().resolve("rest/masstruncate")) )) { testSecured(request, not(either(equalTo(401)).or(equalTo(405)))); } } @Test public void testGetLayer() throws Exception { doGetXML( "rest/layers/img+states.xml", admin.getClient(), equalTo(200), doc->{ assertThat(doc, hasXPath("/wmsLayer/name", equalTo("img states"))); assertThat(doc, hasXPath("/wmsLayer/wmsUrl/string", equalTo("http://demo.opengeo.org/geoserver/wms?"))); assertThat(doc, hasXPath("/wmsLayer/wmsLayers", equalTo("nurc:Img_Sample,topp:states"))); }); } @Test public void testLayerNoAuth() throws Exception { for(CloseableHttpClient client: Arrays.asList( anonymous.getClient(), notAUser.getClient() )){ doGetXML( "rest/layers/img+states.xml", client, equalTo(401), doc->{ assertThat(doc, not( hasXPath("//wmsUrl", containsString("demo.opengeo.org")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("nurc")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("Img_Sample")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("topp")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("states")))); }); } } /** * Check that the given request gives a 401 Forbidden when not authenticated, and otherwise * has a response matching the given matcher * @param request * @param authenticatedStatus * @throws Exception */ protected void testSecured(HttpUriRequest request, Matcher<Integer> authenticatedStatus) throws Exception { { CloseableHttpClient client = admin.getClient(); try( CloseableHttpResponse response = client.execute(request); InputStream in = response.getEntity().getContent()) { assertThat(response.getStatusLine(), hasProperty("statusCode", authenticatedStatus)); } } for(CloseableHttpClient client: Arrays.asList( anonymous.getClient(), notAUser.getClient(), badPassword.getClient() )) { try( CloseableHttpResponse response = client.execute(request); InputStream in = response.getEntity().getContent()) { final int code = 401; assertThat(response.getStatusLine(), describedAs("Request %0 with without authentication produces status code %1", hasProperty("statusCode", equalTo(code)), request, code)); } } } @Test public void testAddLayer() throws Exception { doGetXML( "rest/layers/img+states.xml", notAUser.getClient(), equalTo(401), doc->{ assertThat(doc, not( hasXPath("//wmsUrl", containsString("demo.opengeo.org")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("nurc")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("Img_Sample")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("topp")))); assertThat(doc, not( hasXPath("//wmsLayer", containsString("states")))); }); } interface Assertions<T> { public void accept(T result) throws Exception; } void doGetXML(String uri, CloseableHttpClient client, Matcher<Integer> statusMatcher, Assertions<Document> body) throws Exception { doGetXML(URI.create(uri), client, statusMatcher, body); } void doGetXML(URI uri, CloseableHttpClient client, Matcher<Integer> statusMatcher, Assertions<Document> body) throws Exception { final HttpGet request = new HttpGet(jetty.getUri().resolve(uri)); final Document doc; try( CloseableHttpResponse response = client.execute(request); InputStream in = response.getEntity().getContent()) { doc = XMLUnit.buildTestDocument(new InputSource(in)); assertThat(response.getStatusLine(), hasProperty("statusCode",statusMatcher)); } body.accept(doc); } }