http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java ---------------------------------------------------------------------- diff --cc gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java index db66c8e,0000000..dc76005 mode 100644,000000..100644 --- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java +++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java @@@ -1,52 -1,0 +1,307 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.knox.gateway.service.knoxtoken; + ++import org.apache.knox.gateway.service.knoxtoken.TokenResource; ++import org.apache.knox.gateway.services.GatewayServices; ++import org.apache.knox.gateway.services.security.token.JWTokenAuthority; ++import org.apache.knox.gateway.services.security.token.TokenServiceException; ++import org.apache.knox.gateway.services.security.token.impl.JWT; ++import org.apache.knox.gateway.services.security.token.impl.JWTToken; ++import org.easymock.EasyMock; +import org.junit.Assert; ++import org.junit.BeforeClass; +import org.junit.Test; + ++import com.nimbusds.jose.JWSSigner; ++import com.nimbusds.jose.JWSVerifier; ++import com.nimbusds.jose.crypto.RSASSASigner; ++import com.nimbusds.jose.crypto.RSASSAVerifier; ++ +import java.util.Map; ++ ++import javax.security.auth.Subject; ++import javax.servlet.ServletContext; ++import javax.servlet.http.HttpServletRequest; ++import javax.servlet.http.HttpServletResponse; ++import javax.ws.rs.core.Response; ++ ++import static org.junit.Assert.*; ++ ++import java.io.PrintWriter; ++import java.io.StringWriter; ++import java.security.KeyPair; ++import java.security.KeyPairGenerator; ++import java.security.NoSuchAlgorithmException; ++import java.security.Principal; ++import java.security.interfaces.RSAPrivateKey; ++import java.security.interfaces.RSAPublicKey; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Collections; +import java.util.HashMap; ++import java.util.List; + +/** - * ++ * Some tests for the token service + */ +public class TokenServiceResourceTest { + ++ protected static RSAPublicKey publicKey; ++ protected static RSAPrivateKey privateKey; ++ ++ @BeforeClass ++ public static void setup() throws Exception, NoSuchAlgorithmException { ++ KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); ++ kpg.initialize(1024); ++ KeyPair KPair = kpg.generateKeyPair(); ++ ++ publicKey = (RSAPublicKey) KPair.getPublic(); ++ privateKey = (RSAPrivateKey) KPair.getPrivate(); ++ } ++ + @Test + public void testTokenService() throws Exception { + Assert.assertTrue(true); + } + + @Test + public void testClientData() throws Exception { + TokenResource tr = new TokenResource(); + + Map<String,Object> clientDataMap = new HashMap<>(); + tr.addClientDataToMap("cookie.name=hadoop-jwt,test=value".split(","), clientDataMap); + Assert.assertTrue(clientDataMap.size() == 2); + + clientDataMap = new HashMap<>(); + tr.addClientDataToMap("cookie.name=hadoop-jwt".split(","), clientDataMap); + Assert.assertTrue(clientDataMap.size() == 1); + + clientDataMap = new HashMap<>(); + tr.addClientDataToMap("".split(","), clientDataMap); + Assert.assertTrue(clientDataMap.size() == 0); + } ++ ++ @Test ++ public void testGetToken() throws Exception { ++ TokenResource tr = new TokenResource(); ++ ++ ServletContext context = EasyMock.createNiceMock(ServletContext.class); ++ //tr.context = context; ++ // tr.init(); ++ ++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); ++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); ++ Principal principal = EasyMock.createNiceMock(Principal.class); ++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes(); ++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes(); ++ ++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); ++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); ++ ++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); ++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); ++ ++ StringWriter writer = new StringWriter(); ++ PrintWriter printWriter = new PrintWriter(writer); ++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); ++ EasyMock.expect(response.getWriter()).andReturn(printWriter); ++ ++ EasyMock.replay(principal, services, context, request, response); ++ ++ tr.request = request; ++ tr.response = response; ++ ++ // Issue a token ++ Response retResponse = tr.doGet(); ++ ++ assertEquals(200, retResponse.getStatus()); ++ ++ // Parse the response ++ String retString = writer.toString(); ++ String accessToken = getTagValue(retString, "access_token"); ++ assertNotNull(accessToken); ++ String expiry = getTagValue(retString, "expires_in"); ++ assertNotNull(expiry); ++ ++ // Verify the token ++ JWTToken parsedToken = new JWTToken(accessToken); ++ assertEquals("alice", parsedToken.getSubject()); ++ assertTrue(authority.verifyToken(parsedToken)); ++ } ++ ++ @Test ++ public void testAudiences() throws Exception { ++ ++ ServletContext context = EasyMock.createNiceMock(ServletContext.class); ++ EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2"); ++ EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null); ++ EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null); ++ EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null); ++ ++ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class); ++ EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes(); ++ Principal principal = EasyMock.createNiceMock(Principal.class); ++ EasyMock.expect(principal.getName()).andReturn("alice").anyTimes(); ++ EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes(); ++ ++ GatewayServices services = EasyMock.createNiceMock(GatewayServices.class); ++ EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services); ++ ++ JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey); ++ EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority); ++ ++ StringWriter writer = new StringWriter(); ++ PrintWriter printWriter = new PrintWriter(writer); ++ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class); ++ EasyMock.expect(response.getWriter()).andReturn(printWriter); ++ ++ EasyMock.replay(principal, services, context, request, response); ++ ++ TokenResource tr = new TokenResource(); ++ tr.request = request; ++ tr.response = response; ++ tr.context = context; ++ tr.init(); ++ ++ // Issue a token ++ Response retResponse = tr.doGet(); ++ ++ assertEquals(200, retResponse.getStatus()); ++ ++ // Parse the response ++ String retString = writer.toString(); ++ String accessToken = getTagValue(retString, "access_token"); ++ assertNotNull(accessToken); ++ String expiry = getTagValue(retString, "expires_in"); ++ assertNotNull(expiry); ++ ++ // Verify the token ++ JWTToken parsedToken = new JWTToken(accessToken); ++ assertEquals("alice", parsedToken.getSubject()); ++ assertTrue(authority.verifyToken(parsedToken)); ++ ++ // Verify the audiences ++ List<String> audiences = Arrays.asList(parsedToken.getAudienceClaims()); ++ assertEquals(2, audiences.size()); ++ assertTrue(audiences.contains("recipient1")); ++ assertTrue(audiences.contains("recipient2")); ++ } ++ ++ private String getTagValue(String token, String tagName) { ++ String searchString = tagName + "\":"; ++ String value = token.substring(token.indexOf(searchString) + searchString.length()); ++ if (value.startsWith("\"")) { ++ value = value.substring(1); ++ } ++ if (value.contains("\"")) { ++ return value.substring(0, value.indexOf("\"")); ++ } else if (value.contains(",")) { ++ return value.substring(0, value.indexOf(",")); ++ } else { ++ return value.substring(0, value.length() - 1); ++ } ++ } ++ ++ private static class TestJWTokenAuthority implements JWTokenAuthority { ++ ++ private RSAPublicKey publicKey; ++ private RSAPrivateKey privateKey; ++ ++ public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) { ++ this.publicKey = publicKey; ++ this.privateKey = privateKey; ++ } ++ ++ @Override ++ public JWTToken issueToken(Subject subject, String algorithm) ++ throws TokenServiceException { ++ Principal p = (Principal) subject.getPrincipals().toArray()[0]; ++ return issueToken(p, algorithm); ++ } ++ ++ @Override ++ public JWTToken issueToken(Principal p, String algorithm) ++ throws TokenServiceException { ++ return issueToken(p, null, algorithm); ++ } ++ ++ @Override ++ public JWTToken issueToken(Principal p, String audience, String algorithm) ++ throws TokenServiceException { ++ return issueToken(p, audience, algorithm, -1); ++ } ++ ++ @Override ++ public boolean verifyToken(JWTToken token) throws TokenServiceException { ++ JWSVerifier verifier = new RSASSAVerifier(publicKey); ++ return token.verify(verifier); ++ } ++ ++ @Override ++ public JWTToken issueToken(Principal p, String audience, String algorithm, ++ long expires) throws TokenServiceException { ++ ArrayList<String> audiences = null; ++ if (audience != null) { ++ audiences = new ArrayList<String>(); ++ audiences.add(audience); ++ } ++ return issueToken(p, audiences, algorithm, expires); ++ } ++ ++ @Override ++ public JWTToken issueToken(Principal p, List<String> audiences, String algorithm, ++ long expires) throws TokenServiceException { ++ String[] claimArray = new String[4]; ++ claimArray[0] = "KNOXSSO"; ++ claimArray[1] = p.getName(); ++ claimArray[2] = null; ++ if (expires == -1) { ++ claimArray[3] = null; ++ } else { ++ claimArray[3] = String.valueOf(expires); ++ } ++ ++ JWTToken token = null; ++ if ("RS256".equals(algorithm)) { ++ token = new JWTToken("RS256", claimArray, audiences); ++ JWSSigner signer = new RSASSASigner(privateKey); ++ token.sign(signer); ++ } else { ++ throw new TokenServiceException("Cannot issue token - Unsupported algorithm"); ++ } ++ ++ return token; ++ } ++ ++ @Override ++ public JWT issueToken(Principal p, String algorithm, long expiry) ++ throws TokenServiceException { ++ return issueToken(p, Collections.<String>emptyList(), algorithm, expiry); ++ } ++ ++ @Override ++ public boolean verifyToken(JWTToken token, RSAPublicKey publicKey) throws TokenServiceException { ++ JWSVerifier verifier = new RSASSAVerifier(publicKey); ++ return token.verify(verifier); ++ } ++ ++ } ++ ++ +}
http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java ---------------------------------------------------------------------- diff --cc gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java index 8b26dce,0000000..79837e8 mode 100644,000000..100644 --- a/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java +++ b/gateway-test/src/test/java/org/apache/knox/gateway/AmbariServiceDefinitionTest.java @@@ -1,354 -1,0 +1,354 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * <p/> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p/> + * 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.apache.knox.gateway; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.knox.gateway.services.DefaultGatewayServices; +import org.apache.knox.gateway.services.GatewayServices; +import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.knox.gateway.services.topology.TopologyService; +import org.apache.hadoop.test.TestUtils; +import org.apache.hadoop.test.mock.MockServer; +import org.apache.http.HttpStatus; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; +import org.hamcrest.MatcherAssert; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +import static io.restassured.RestAssured.given; +import static org.apache.hadoop.test.TestUtils.LOG_ENTER; +import static org.apache.hadoop.test.TestUtils.LOG_EXIT; +import static org.hamcrest.CoreMatchers.notNullValue; +import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; + +public class AmbariServiceDefinitionTest { + + private static Logger LOG = LoggerFactory.getLogger( AmbariServiceDefinitionTest.class ); + private static Class<?> DAT = AmbariServiceDefinitionTest.class; + + private static GatewayTestConfig config; + private static DefaultGatewayServices services; + private static GatewayServer gateway; + private static int gatewayPort; + private static String gatewayUrl; + private static String clusterUrl; + private static String clusterPath; + private static Properties params; + private static TopologyService topos; + private static MockServer mockAmbari; + + private static VelocityEngine velocity; + private static VelocityContext context; + + @BeforeClass + public static void setupSuite() throws Exception { + LOG_ENTER(); + setupGateway(); + String topoStr = TestUtils.merge( DAT, "test-topology.xml", params ); + File topoFile = new File( config.getGatewayTopologyDir(), "test-topology.xml" ); + FileUtils.writeStringToFile( topoFile, topoStr ); + topos.reloadTopologies(); + LOG_EXIT(); + } + + @AfterClass + public static void cleanupSuite() throws Exception { + LOG_ENTER(); + gateway.stop(); + FileUtils.deleteQuietly( new File( config.getGatewayHomeDir() ) ); + LOG_EXIT(); + } + + @After + public void cleanupTest() throws Exception { + FileUtils.cleanDirectory( new File( config.getGatewayTopologyDir() ) ); + // Test run should not fail if deleting deployment files is not successful. + // Deletion has been already done by TopologyService. + FileUtils.deleteQuietly( new File( config.getGatewayDeploymentDir() ) ); + } + + public static void setupGateway() throws Exception { + File targetDir = new File( System.getProperty( "user.dir" ), "target" ); + File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() ); + gatewayDir.mkdirs(); + + config = new GatewayTestConfig(); + config.setGatewayHomeDir( gatewayDir.getAbsolutePath() ); + + File topoDir = new File( config.getGatewayTopologyDir() ); + topoDir.mkdirs(); + + File deployDir = new File( config.getGatewayDeploymentDir() ); + deployDir.mkdirs(); + + setupMockServers(); + startGatewayServer(); + } + + public static void setupMockServers() throws Exception { + mockAmbari = new MockServer( "AMBARI", true ); + } + + public static void startGatewayServer() throws Exception { + services = new DefaultGatewayServices(); + Map<String,String> options = new HashMap<>(); + options.put( "persist-master", "false" ); + options.put( "master", "password" ); + try { + services.init( config, options ); + } catch ( ServiceLifecycleException e ) { + e.printStackTrace(); // I18N not required. + } + topos = services.getService(GatewayServices.TOPOLOGY_SERVICE); + + gateway = GatewayServer.startGateway( config, services ); + MatcherAssert.assertThat( "Failed to start gateway.", gateway, notNullValue() ); + + gatewayPort = gateway.getAddresses()[0].getPort(); + gatewayUrl = "http://localhost:" + gatewayPort + "/" + config.getGatewayPath(); + String topologyPath = "/test-topology"; + clusterPath = "/" + config.getGatewayPath() + topologyPath; + clusterUrl = gatewayUrl + topologyPath; + + LOG.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() ); + + params = new Properties(); + params.put( "AMBARI_URL", "http://localhost:" + mockAmbari.getPort() ); + + velocity = new VelocityEngine(); + velocity.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.NullLogSystem" ); + velocity.setProperty( RuntimeConstants.RESOURCE_LOADER, "classpath" ); + velocity.setProperty( "classpath.resource.loader.class", ClasspathResourceLoader.class.getName() ); + velocity.init(); + + context = new VelocityContext(); + context.put( "cluster_url", clusterUrl ); + context.put( "cluster_path", clusterPath ); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void clusters() throws Exception { + LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/ambari/api/v1/clusters"; + + mockAmbari.expect() + .method( "GET" ) + .pathInfo( "/api/v1/clusters" ) + .respond() + .status( HttpStatus.SC_OK ) + .content( TestUtils.getResourceStream( DAT, "clusters-response.json" ) ) + .contentType( "text/plain" ); + + String body = given() +// .log().all() + .auth().preemptive().basic( username, password ) - .expect() ++ .then() +// .log().all() + .statusCode( HttpStatus.SC_OK ) + .contentType( "text/plain" ) + .when().get( serviceUrl ).asString(); + + + String name = TestUtils.getResourceName( this.getClass(), "clusters-response-expected.json" ); + Template template = velocity.getTemplate( name ); + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + String expected = sw.toString(); + + MatcherAssert.assertThat(body, sameJSONAs(expected)); + LOG_EXIT(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void historyServer() throws Exception { + LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/ambari/api/v1/clusters/test/hosts/c6401.ambari.apache.org/host_components/HISTORYSERVER"; + + mockAmbari.expect() + .method( "GET" ) + .pathInfo( "/api/v1/clusters/test/hosts/c6401.ambari.apache.org/host_components/HISTORYSERVER" ) + .respond() + .status( HttpStatus.SC_OK ) + .content( TestUtils.getResourceStream( DAT, "history-server-response.json" ) ) + .contentType( "text/plain" ); + + String body = given() + .auth().preemptive().basic( username, password ) - .expect() ++ .then() + .statusCode( HttpStatus.SC_OK ) + .contentType( "text/plain" ) + .when().get( serviceUrl ).asString(); + + + String name = TestUtils.getResourceName( this.getClass(), "history-server-response-expected.json" ); + Template template = velocity.getTemplate( name ); + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + String expected = sw.toString(); + + MatcherAssert.assertThat(body, sameJSONAs(expected)); + LOG_EXIT(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void unwiseCharacterRequest() throws Exception { + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/ambari/api/v1/clusters/test/components"; + + mockAmbari.expect() + .method( "GET" ) + .pathInfo( "/api/v1/clusters/test/components" ) + .queryParam("ServiceComponentInfo/component_name", "APP_TIMELINE_SERVER|ServiceComponentInfo/category=MASTER") + .respond() + .status( HttpStatus.SC_OK ) + .content( TestUtils.getResourceStream( DAT, "unwise-character-response.json" ) ) + .contentType( "text/plain" ); + //only assertion here is to make sure the request can be made successfully with the unwise characters present + //in the request url + given() + .auth().preemptive().basic( username, password ) + .queryParam("ServiceComponentInfo/component_name", "APP_TIMELINE_SERVER|ServiceComponentInfo/category=MASTER") - .expect() ++ .then() + .statusCode( HttpStatus.SC_OK ) + .contentType( "text/plain" ) + .when().get( serviceUrl ).asString(); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void encryptedResponse() throws Exception { + LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/ambari/api/v1/persist/CLUSTER_CURRENT_STATUS?_=1457977721091"; + + mockAmbari.expect() + .method( "GET" ) + .pathInfo( "/api/v1/persist/CLUSTER_CURRENT_STATUS" ) + .queryParam("_","1457977721091") + .respond() + .status( HttpStatus.SC_OK ) + .content( TestUtils.getResourceStream( DAT, "encrypted-response.txt" ) ) + .contentType( "text/plain" ); + + String body = given() + .auth().preemptive().basic( username, password ) - .expect() ++ .then() + .statusCode( HttpStatus.SC_OK ) + .contentType( "text/plain" ) + .when().get( serviceUrl ).asString(); + + Assert.assertNotNull(body); + LOG_EXIT(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void postDataWithWrongContentType() throws Exception { + LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/ambari/api/v1/stacks/HDP/versions/2.3/recommendations"; + + mockAmbari.expect() + .method( "POST" ) + .pathInfo( "/api/v1/stacks/HDP/versions/2.3/recommendations" ) + .content( TestUtils.getResourceStream( DAT, "post-data-wrong-type.json" ) ) + .respond() + .status( HttpStatus.SC_OK ) + .contentType( "application/x-www-form-urlencoded" ); + + + String body = given() + .auth().preemptive().basic( username, password ) - .content(IOUtils.toByteArray(TestUtils.getResourceStream( DAT, "post-data-wrong-type.json"))) - .expect() ++ .body(IOUtils.toByteArray(TestUtils.getResourceStream( DAT, "post-data-wrong-type.json"))) ++ .then() + .statusCode( HttpStatus.SC_OK ) + .contentType( "application/x-www-form-urlencoded" ) + .when().post( serviceUrl ).asString(); + + Assert.assertNotNull(body); + LOG_EXIT(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void contextPathInViewsResponse() throws Exception { + LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + + String serviceUrl = clusterUrl + "/ambari/api/v1/views?fields=versions/instances/ViewInstanceInfo,versions/" + + "ViewVersionInfo/label&versions/ViewVersionInfo/system=false&_=1461186937589"; + + mockAmbari.expect() + .method( "GET" ) + .pathInfo( "/api/v1/views" ) + .queryParam("_", "1461186937589") + .queryParam("versions/ViewVersionInfo/system", "false") + .queryParam("fields", "versions/instances/ViewInstanceInfo,versions/ViewVersionInfo/label") + .respond() + .status( HttpStatus.SC_OK ) + .content( TestUtils.getResourceStream( DAT, "views-response.json" ) ) + .contentType( "text/plain" ); + + String body = given() + .auth().preemptive().basic( username, password ) - .expect() ++ .then() + .statusCode( HttpStatus.SC_OK ) + .contentType( "text/plain" ) + .when().get( serviceUrl ).asString(); + + + String name = TestUtils.getResourceName( this.getClass(), "views-response-expected.json" ); + Template template = velocity.getTemplate( name ); + StringWriter sw = new StringWriter(); + template.merge( context, sw ); + String expected = sw.toString(); + + MatcherAssert.assertThat(body, sameJSONAs(expected)); + LOG_EXIT(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java ---------------------------------------------------------------------- diff --cc gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java index 810626b,0000000..eba5de6 mode 100644,000000..100644 --- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java +++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminFuncTest.java @@@ -1,176 -1,0 +1,176 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.knox.gateway; + +import com.mycila.xmltool.XMLDoc; +import com.mycila.xmltool.XMLTag; +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.services.DefaultGatewayServices; +import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.hadoop.test.TestUtils; +import org.apache.http.HttpStatus; +import org.hamcrest.MatcherAssert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.core.MediaType; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.notNullValue; + +public class GatewayAdminFuncTest { + + private static Logger LOG = LoggerFactory.getLogger( GatewayAdminFuncTest.class ); + + //public static Enumeration<Appender> appenders; + public static GatewayConfig config; + public static GatewayServer gateway; + public static String gatewayUrl; + public static String clusterUrl; + private static GatewayTestDriver driver = new GatewayTestDriver(); + + @BeforeClass + public static void setupSuite() throws Exception { + TestUtils.LOG_ENTER(); + //appenders = NoOpAppender.setUp(); + driver.setupLdap(0); + setupGateway(); + TestUtils.LOG_EXIT(); + } + + @AfterClass + public static void cleanupSuite() throws Exception { + TestUtils.LOG_ENTER(); + gateway.stop(); + driver.cleanup(); + //FileUtils.deleteQuietly( new File( config.getGatewayHomeDir() ) ); + //NoOpAppender.tearDown( appenders ); + TestUtils.LOG_EXIT(); + } + + public static void setupGateway() throws Exception { + + File targetDir = new File( System.getProperty( "user.dir" ), "target" ); + File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() ); + gatewayDir.mkdirs(); + + GatewayTestConfig testConfig = new GatewayTestConfig(); + config = testConfig; + testConfig.setGatewayHomeDir( gatewayDir.getAbsolutePath() ); + + File topoDir = new File( testConfig.getGatewayTopologyDir() ); + topoDir.mkdirs(); + + File deployDir = new File( testConfig.getGatewayDeploymentDir() ); + deployDir.mkdirs(); + + File descriptor = new File( topoDir, "test-cluster.xml" ); + FileOutputStream stream = new FileOutputStream( descriptor ); + createTopology().toStream( stream ); + stream.close(); + + DefaultGatewayServices srvcs = new DefaultGatewayServices(); + Map<String,String> options = new HashMap<>(); + options.put( "persist-master", "false" ); + options.put( "master", "password" ); + try { + srvcs.init( testConfig, options ); + } catch ( ServiceLifecycleException e ) { + e.printStackTrace(); // I18N not required. + } + gateway = GatewayServer.startGateway( testConfig, srvcs ); + MatcherAssert.assertThat( "Failed to start gateway.", gateway, notNullValue() ); + + LOG.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() ); + + gatewayUrl = "http://localhost:" + gateway.getAddresses()[0].getPort() + "/" + config.getGatewayPath(); + clusterUrl = gatewayUrl + "/test-cluster"; + } + + private static XMLTag createTopology() { + XMLTag xml = XMLDoc.newDocument( true ) + .addRoot( "topology" ) + .addTag( "gateway" ) + .addTag( "provider" ) + .addTag( "role" ).addText( "authentication" ) + .addTag( "name" ).addText( "ShiroProvider" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm" ) + .addTag( "value" ).addText( "org.apache.knox.gateway.shirorealm.KnoxLdapRealm" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.userDnTemplate" ) + .addTag( "value" ).addText( "uid={0},ou=people,dc=hadoop,dc=apache,dc=org" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.url" ) + .addTag( "value" ).addText( driver.getLdapUrl() ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.authenticationMechanism" ) + .addTag( "value" ).addText( "simple" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "urls./**" ) + .addTag( "value" ).addText( "authcBasic" ).gotoParent().gotoParent() + .addTag( "provider" ) + .addTag( "role" ).addText( "identity-assertion" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "name" ).addText( "Default" ).gotoParent() + .addTag( "provider" ) + .gotoRoot() + .addTag( "service" ) + .addTag( "role" ).addText( "KNOX" ) + .gotoRoot(); + // System.out.println( "GATEWAY=" + xml.toString() ); + return xml; + } + + //@Test + public void waitForManualTesting() throws IOException { + System.out.println( clusterUrl ); + System.in.read(); + } + + @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) + public void testAdminService() throws ClassNotFoundException { + TestUtils.LOG_ENTER(); + + String username = "guest"; + String password = "guest-password"; + String serviceUrl = clusterUrl + "/api/v1/version"; + given() + //.log().all() + .auth().preemptive().basic( username, password ) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + //.body( is( "{\"hash\":\"unknown\",\"version\":\"unknown\"}" ) ) + .when().get( serviceUrl ); + + TestUtils.LOG_EXIT(); + } + +} http://git-wip-us.apache.org/repos/asf/knox/blob/416ee7c1/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java ---------------------------------------------------------------------- diff --cc gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java index e5c16be,0000000..d33d59e mode 100644,000000..100644 --- a/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java +++ b/gateway-test/src/test/java/org/apache/knox/gateway/GatewayAdminTopologyFuncTest.java @@@ -1,802 -1,0 +1,800 @@@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.knox.gateway; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import javax.ws.rs.core.MediaType; + +import io.restassured.http.ContentType; +import com.mycila.xmltool.XMLDoc; +import com.mycila.xmltool.XMLTag; +import org.apache.directory.server.protocol.shared.transport.TcpTransport; +import org.apache.knox.gateway.config.GatewayConfig; +import org.apache.knox.gateway.security.ldap.SimpleLdapDirectoryServer; +import org.apache.knox.gateway.services.DefaultGatewayServices; +import org.apache.knox.gateway.services.GatewayServices; +import org.apache.knox.gateway.services.ServiceLifecycleException; +import org.apache.knox.gateway.services.topology.TopologyService; +import org.apache.knox.gateway.topology.Param; +import org.apache.knox.gateway.topology.Provider; +import org.apache.knox.gateway.topology.Service; +import org.apache.knox.gateway.topology.Topology; +import org.apache.knox.gateway.util.XmlUtils; +import org.apache.hadoop.test.TestUtils; +import org.apache.http.HttpStatus; +import org.apache.log4j.Appender; +import org.hamcrest.MatcherAssert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import static io.restassured.RestAssured.given; +import static org.apache.hadoop.test.TestUtils.LOG_ENTER; +import static org.apache.hadoop.test.TestUtils.LOG_EXIT; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.xml.HasXPath.hasXPath; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +public class GatewayAdminTopologyFuncTest { + + private static Logger LOG = LoggerFactory.getLogger( GatewayAdminTopologyFuncTest.class ); + + public static Enumeration<Appender> appenders; + public static GatewayConfig config; + public static GatewayServer gateway; + public static String gatewayUrl; + public static String clusterUrl; + private static GatewayTestDriver driver = new GatewayTestDriver(); + + @BeforeClass + public static void setupSuite() throws Exception { + //appenders = NoOpAppender.setUp(); + driver.setupLdap(0); + setupGateway(new GatewayTestConfig()); + } + + @AfterClass + public static void cleanupSuite() throws Exception { + gateway.stop(); + driver.cleanup(); + //FileUtils.deleteQuietly( new File( config.getGatewayHomeDir() ) ); + //NoOpAppender.tearDown( appenders ); + } + + public static void setupGateway(GatewayTestConfig testConfig) throws Exception { + + File targetDir = new File( System.getProperty( "user.dir" ), "target" ); + File gatewayDir = new File( targetDir, "gateway-home-" + UUID.randomUUID() ); + gatewayDir.mkdirs(); + + config = testConfig; + testConfig.setGatewayHomeDir( gatewayDir.getAbsolutePath() ); + + File topoDir = new File( testConfig.getGatewayTopologyDir() ); + topoDir.mkdirs(); + + File deployDir = new File( testConfig.getGatewayDeploymentDir() ); + deployDir.mkdirs(); + + File descriptor = new File( topoDir, "admin.xml" ); + FileOutputStream stream = new FileOutputStream( descriptor ); + createKnoxTopology().toStream( stream ); + stream.close(); + + File descriptor2 = new File( topoDir, "test-cluster.xml" ); + FileOutputStream stream2 = new FileOutputStream( descriptor2 ); + createNormalTopology().toStream( stream2 ); + stream.close(); + + DefaultGatewayServices srvcs = new DefaultGatewayServices(); + Map<String,String> options = new HashMap<>(); + options.put( "persist-master", "false" ); + options.put( "master", "password" ); + + try { + srvcs.init( testConfig, options ); + } catch ( ServiceLifecycleException e ) { + e.printStackTrace(); // I18N not required. + } + gateway = GatewayServer.startGateway( testConfig, srvcs ); + MatcherAssert.assertThat( "Failed to start gateway.", gateway, notNullValue() ); + + LOG.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() ); + + gatewayUrl = "http://localhost:" + gateway.getAddresses()[0].getPort() + "/" + config.getGatewayPath(); + clusterUrl = gatewayUrl + "/admin"; + } + + private static XMLTag createNormalTopology() { + XMLTag xml = XMLDoc.newDocument( true ) + .addRoot( "topology" ) + .addTag( "gateway" ) + .addTag( "provider" ) + .addTag( "role" ).addText( "webappsec" ) + .addTag( "name" ).addText( "WebAppSec" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "param" ) + .addTag( "name" ).addText( "csrf.enabled" ) + .addTag( "value" ).addText( "true" ).gotoParent().gotoParent() + .addTag( "provider" ) + .addTag( "role" ).addText( "authentication" ) + .addTag( "name" ).addText( "ShiroProvider" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm" ) + .addTag( "value" ).addText( "org.apache.knox.gateway.shirorealm.KnoxLdapRealm" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.userDnTemplate" ) + .addTag( "value" ).addText( "uid={0},ou=people,dc=hadoop,dc=apache,dc=org" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.url" ) + .addTag( "value" ).addText( driver.getLdapUrl() ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.authenticationMechanism" ) + .addTag( "value" ).addText( "simple" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "urls./**" ) + .addTag( "value" ).addText( "authcBasic" ).gotoParent().gotoParent() + .addTag( "provider" ) + .addTag( "role" ).addText( "identity-assertion" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "name" ).addText( "Default" ).gotoParent() + .addTag( "provider" ) + .addTag( "role" ).addText( "authorization" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "name" ).addText( "AclsAuthz" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "webhdfs-acl" ) + .addTag( "value" ).addText( "hdfs;*;*" ).gotoParent() + .gotoRoot() + .addTag( "service" ) + .addTag( "role" ).addText( "WEBHDFS" ) + .addTag( "url" ).addText( "http://localhost:50070/webhdfs/v1" ).gotoParent() + .gotoRoot(); +// System.out.println( "GATEWAY=" + xml.toString() ); + return xml; + } + + private static XMLTag createKnoxTopology() { + XMLTag xml = XMLDoc.newDocument( true ) + .addRoot( "topology" ) + .addTag( "gateway" ) + .addTag( "provider" ) + .addTag( "role" ).addText( "authentication" ) + .addTag( "name" ).addText( "ShiroProvider" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm" ) + .addTag( "value" ).addText( "org.apache.knox.gateway.shirorealm.KnoxLdapRealm" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.userDnTemplate" ) + .addTag( "value" ).addText( "uid={0},ou=people,dc=hadoop,dc=apache,dc=org" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.url" ) + .addTag( "value" ).addText( driver.getLdapUrl() ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "main.ldapRealm.contextFactory.authenticationMechanism" ) + .addTag( "value" ).addText( "simple" ).gotoParent() + .addTag( "param" ) + .addTag( "name" ).addText( "urls./**" ) + .addTag( "value" ).addText( "authcBasic" ).gotoParent().gotoParent() + .addTag("provider") + .addTag( "role" ).addText( "authorization" ) + .addTag( "name" ).addText( "AclsAuthz" ) + .addTag( "enabled" ).addText( "true" ) + .addTag("param") + .addTag("name").addText("knox.acl") + .addTag("value").addText("admin;*;*").gotoParent().gotoParent() + .addTag("provider") + .addTag( "role" ).addText( "identity-assertion" ) + .addTag( "enabled" ).addText( "true" ) + .addTag( "name" ).addText( "Default" ).gotoParent() + .gotoRoot() + .addTag( "service" ) + .addTag( "role" ).addText( "KNOX" ) + .gotoRoot(); + // System.out.println( "GATEWAY=" + xml.toString() ); + return xml; + } + + //@Test + public void waitForManualTesting() throws IOException { + System.in.read(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testTopologyCollection() throws ClassNotFoundException { + LOG_ENTER(); + + String username = "admin"; + String password = "admin-password"; + String serviceUrl = clusterUrl + "/api/v1/topologies"; + String href1 = given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .body("topologies.topology[0].name", not(nullValue())) + .body("topologies.topology[1].name", not(nullValue())) + .body("topologies.topology[0].uri", not(nullValue())) + .body("topologies.topology[1].uri", not(nullValue())) + .body("topologies.topology[0].href", not(nullValue())) + .body("topologies.topology[1].href", not(nullValue())) + .body("topologies.topology[0].timestamp", not(nullValue())) + .body("topologies.topology[1].timestamp", not(nullValue())) + .when().get(serviceUrl).thenReturn().getBody().path("topologies.topology.href[1]"); + + given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + //.log().all() + .body("topologies.topology.href[1]", equalTo(href1)) + .statusCode(HttpStatus.SC_OK) + .when().get(serviceUrl); + + + given() + //.log().all() + .auth().preemptive().basic(username, password) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType(MediaType.APPLICATION_XML) + .when().get(serviceUrl); + + + given().auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType("application/json") + .body("topology.name", equalTo("test-cluster")) + .when().get(href1); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testTopologyObject() throws ClassNotFoundException { + LOG_ENTER(); + + String username = "admin"; + String password = "admin-password"; + String serviceUrl = clusterUrl + "/api/v1/topologies"; + String hrefJson = given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .when().get(serviceUrl).thenReturn().getBody().path("topologies.topology[1].href"); + + String timestampJson = given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType("application/json") + .when().get(serviceUrl).andReturn() + .getBody().path("topologies.topology[1].timestamp"); + + given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .body("topology.name", equalTo("test-cluster")) + .body("topology.timestamp", equalTo(Long.parseLong(timestampJson))) + .when() + .get(hrefJson); + + + String hrefXml = given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .when().get(serviceUrl).thenReturn().getBody().path("topologies.topology[1].href"); + + given() + //.log().all() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .when() + .get(hrefXml); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testPositiveAuthorization() throws ClassNotFoundException{ + LOG_ENTER(); + + String adminUser = "admin"; + String adminPass = "admin-password"; + String url = clusterUrl + "/api/v1/topologies"; + + given() + //.log().all() + .auth().preemptive().basic(adminUser, adminPass) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType(ContentType.JSON) + .body("topologies.topology[0].name", not(nullValue())) + .body("topologies.topology[1].name", not(nullValue())) + .body("topologies.topology[0].uri", not(nullValue())) + .body("topologies.topology[1].uri", not(nullValue())) + .body("topologies.topology[0].href", not(nullValue())) + .body("topologies.topology[1].href", not(nullValue())) + .body("topologies.topology[0].timestamp", not(nullValue())) + .body("topologies.topology[1].timestamp", not(nullValue())) + .when().get(url); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testNegativeAuthorization() throws ClassNotFoundException{ + LOG_ENTER(); + + String guestUser = "guest"; + String guestPass = "guest-password"; + String url = clusterUrl + "/api/v1/topologies"; + + given() + //.log().all() + .auth().basic(guestUser, guestPass) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_FORBIDDEN) + .when().get(url); + + LOG_EXIT(); + } + + private Topology createTestTopology(){ + Topology topology = new Topology(); + topology.setName("test-topology"); + + try { + topology.setUri(new URI(gatewayUrl + "/" + topology.getName())); + } catch (URISyntaxException ex) { + assertThat(topology.getUri(), not(nullValue())); + } + + Provider identityProvider = new Provider(); + identityProvider.setName("Default"); + identityProvider.setRole("identity-assertion"); + identityProvider.setEnabled(true); + + Provider AuthenicationProvider = new Provider(); + AuthenicationProvider.setName("ShiroProvider"); + AuthenicationProvider.setRole("authentication"); + AuthenicationProvider.setEnabled(true); + + Param ldapMain = new Param(); + ldapMain.setName("main.ldapRealm"); + ldapMain.setValue("org.apache.knox.gateway.shirorealm.KnoxLdapRealm"); + + Param ldapGroupContextFactory = new Param(); + ldapGroupContextFactory.setName("main.ldapGroupContextFactory"); + ldapGroupContextFactory.setValue("org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory"); + + Param ldapRealmContext = new Param(); + ldapRealmContext.setName("main.ldapRealm.contextFactory"); + ldapRealmContext.setValue("$ldapGroupContextFactory"); + + Param ldapURL = new Param(); + ldapURL.setName("main.ldapRealm.contextFactory.url"); + ldapURL.setValue(driver.getLdapUrl()); + + Param ldapUserTemplate = new Param(); + ldapUserTemplate.setName("main.ldapRealm.userDnTemplate"); + ldapUserTemplate.setValue("uid={0},ou=people,dc=hadoop,dc=apache,dc=org"); + + Param authcBasic = new Param(); + authcBasic.setName("urls./**"); + authcBasic.setValue("authcBasic"); + + AuthenicationProvider.addParam(ldapGroupContextFactory); + AuthenicationProvider.addParam(ldapMain); + AuthenicationProvider.addParam(ldapRealmContext); + AuthenicationProvider.addParam(ldapURL); + AuthenicationProvider.addParam(ldapUserTemplate); + AuthenicationProvider.addParam(authcBasic); + + Service testService = new Service(); + testService.setRole("test-service-role"); + + topology.addProvider(AuthenicationProvider); + topology.addProvider(identityProvider); + topology.addService(testService); + topology.setTimestamp(System.nanoTime()); + + return topology; + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testDeployTopology() throws Exception { + LOG_ENTER(); + + Topology testTopology = createTestTopology(); + + String user = "guest"; + String password = "guest-password"; + + String url = gatewayUrl + "/" + testTopology.getName() + "/test-service-path/test-service-resource"; + + GatewayServices srvs = GatewayServer.getGatewayServices(); + + TopologyService ts = srvs.getService(GatewayServices.TOPOLOGY_SERVICE); + try { + ts.stopMonitor(); + + assertThat( testTopology, not( nullValue() ) ); + assertThat( testTopology.getName(), is( "test-topology" ) ); + + given() + //.log().all() - .auth().preemptive().basic( "admin", "admin-password" ).header( "Accept", MediaType.APPLICATION_JSON ).expect() ++ .auth().preemptive().basic( "admin", "admin-password" ).header( "Accept", MediaType.APPLICATION_JSON ).then() + //.log().all() + .statusCode( HttpStatus.SC_OK ).body( containsString( "ServerVersion" ) ).when().get( gatewayUrl + "/admin/api/v1/version" ); + + given() + //.log().all() - .auth().preemptive().basic( user, password ).expect() ++ .auth().preemptive().basic( user, password ).then() + //.log().all() + .statusCode( HttpStatus.SC_NOT_FOUND ).when().get( url ); + + ts.deployTopology( testTopology ); + + given() + //.log().all() - .auth().preemptive().basic( user, password ).expect() ++ .auth().preemptive().basic( user, password ).then() + //.log().all() + .statusCode( HttpStatus.SC_OK ).contentType( "text/plain" ).body( is( "test-service-response" ) ).when().get( url ).getBody(); + + ts.deleteTopology( testTopology ); + + given() + //.log().all() - .auth().preemptive().basic( user, password ).expect() ++ .auth().preemptive().basic( user, password ).then() + //.log().all() + .statusCode( HttpStatus.SC_NOT_FOUND ).when().get( url ); + } finally { + ts.startMonitor(); + } + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testDeleteTopology() throws ClassNotFoundException { + LOG_ENTER(); + + Topology test = createTestTopology(); + + String username = "admin"; + String password = "admin-password"; + String url = clusterUrl + "/api/v1/topologies/" + test.getName(); + + GatewayServices gs = GatewayServer.getGatewayServices(); + + TopologyService ts = gs.getService(GatewayServices.TOPOLOGY_SERVICE); + + ts.deployTopology(test); + + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType(MediaType.APPLICATION_JSON) + .when().get(url); + + given() + .auth().preemptive().basic(username, password) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_OK) + .contentType(MediaType.APPLICATION_JSON) + .when().delete(url); + + given() + //.log().all() + .auth().preemptive().basic(username, password) - .expect() ++ .then() + //.log().all() + .statusCode(HttpStatus.SC_NO_CONTENT) + .when().get(url); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testPutTopology() throws Exception { + LOG_ENTER() ; + + String username = "admin"; + String password = "admin-password"; + String url = clusterUrl + "/api/v1/topologies/test-put"; + + String JsonPut = + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) + .get(clusterUrl + "/api/v1/topologies/test-cluster") + .getBody().asString(); + + String XML = given() + //.log().all() + .auth().preemptive().basic(username, password) + .contentType(MediaType.APPLICATION_JSON) + .header("Accept", MediaType.APPLICATION_XML) + .body(JsonPut) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + //.log().all() + .when().put(url).getBody().asString(); + + InputSource source = new InputSource( new StringReader( XML ) ); + Document doc = XmlUtils.readXml( source ); + + assertThat( doc, hasXPath( "/topology/gateway/provider[1]/name", containsString( "WebAppSec" ) ) ); + assertThat( doc, hasXPath( "/topology/gateway/provider[1]/param/name", containsString( "csrf.enabled" ) ) ); + + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(equalTo(XML)) + .when().get(url) + .getBody().asString(); + + String XmlPut = + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .get(clusterUrl + "/api/v1/topologies/test-cluster") + .getBody().asString(); + + String JSON = given() + //.log().all() + .auth().preemptive().basic(username, password) + .contentType(MediaType.APPLICATION_XML) + .header("Accept", MediaType.APPLICATION_JSON) + .body(XmlPut) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + //.log().all() + .when().put(url).getBody().asString(); + + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_JSON) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(equalTo(JSON)) + .when().get(url) + .getBody().asString(); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testXForwardedHeaders() { + LOG_ENTER(); + + String username = "admin"; + String password = "admin-password"; + String url = clusterUrl + "/api/v1/topologies"; + +// X-Forward header values + String port = String.valueOf(777); + String server = "myserver"; + String host = server + ":" + port; + String proto = "protocol"; + String context = "/mycontext"; + String newUrl = proto + "://" + host + context; +// String port = String.valueOf(gateway.getAddresses()[0].getPort()); + +// Case 1: Add in all x-forward headers (host, port, server, context, proto) + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .header("X-Forwarded-Host", host ) + .header("X-Forwarded-Port", port ) + .header("X-Forwarded-Server", server ) + .header("X-Forwarded-Context", context) + .header("X-Forwarded-Proto", proto) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(newUrl)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + + +// Case 2: add in x-forward headers (host, server, proto, context) + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .header("X-Forwarded-Host", host ) + .header("X-Forwarded-Server", server ) + .header("X-Forwarded-Context", context ) + .header("X-Forwarded-Proto", proto ) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(server)) + .body(containsString(context)) + .body(containsString(proto)) + .body(containsString(host)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + +// Case 3: add in x-forward headers (host, proto, port, context) + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .header("X-Forwarded-Host", host ) + .header("X-Forwarded-Port", port ) + .header("X-Forwarded-Context", context ) + .header("X-Forwarded-Proto", proto) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(host)) + .body(containsString(port)) + .body(containsString(context)) + .body(containsString(proto)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + +// Case 4: add in x-forward headers (host, proto, port, context) no port in host. + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .header("X-Forwarded-Host", server) + .header("X-Forwarded-Port", port) + .header("X-Forwarded-Context", context) + .header("X-Forwarded-Proto", proto) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(server)) + .body(containsString(port)) + .body(containsString(context)) + .body(containsString(proto)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + +// Case 5: add in x-forward headers (host, port) + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) + .header("X-Forwarded-Host", host ) + .header("X-Forwarded-Port", port ) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(host)) + .body(containsString(port)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + +// Case 6: Normal Request + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(url)) + .body(containsString("test-cluster")) + .body(containsString("admin")) + .when().get(url); + + LOG_EXIT(); + } + + @Test( timeout = TestUtils.LONG_TIMEOUT ) + public void testGatewayPathChange() throws Exception { + LOG_ENTER(); + String username = "admin"; + String password = "admin-password"; + String url = clusterUrl + "/api/v1/topologies"; + +// Case 1: Normal Request (No Change in gateway.path). Ensure HTTP OK resp + valid URL. + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(url + "/test-cluster")) + .when().get(url); + + +// Case 2: Change gateway.path to another String. Ensure HTTP OK resp + valid URL. + try { + gateway.stop(); + + GatewayTestConfig conf = new GatewayTestConfig(); + conf.setGatewayPath("new-gateway-path"); + setupGateway(conf); + + String newUrl = clusterUrl + "/api/v1/topologies"; + + given() + .auth().preemptive().basic(username, password) + .header("Accept", MediaType.APPLICATION_XML) - .expect() ++ .then() + .statusCode(HttpStatus.SC_OK) + .body(containsString(newUrl + "/test-cluster")) + .when().get(newUrl); + } catch(Exception e){ + fail(e.getMessage()); + } + finally { +// Restart the gateway with old settings. + gateway.stop(); + setupGateway(new GatewayTestConfig()); + } + + LOG_EXIT(); + } + - private static final String CLASS = GatewayAdminTopologyFuncTest.class.getCanonicalName(); - +}