/** * 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.hadoop.gateway; import java.io.File; import java.net.URL; import java.nio.charset.Charset; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.directory.server.protocol.shared.transport.TcpTransport; import org.apache.hadoop.gateway.security.ldap.SimpleLdapDirectoryServer; import org.apache.hadoop.gateway.services.DefaultGatewayServices; import org.apache.hadoop.gateway.services.GatewayServices; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.topology.TopologyService; import org.apache.hadoop.test.TestUtils; import org.apache.hadoop.test.category.ReleaseTest; import org.apache.hadoop.test.mock.MockServer; import org.apache.http.HttpHost; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.log4j.Appender; import org.hamcrest.MatcherAssert; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.jayway.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.endsWith; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; import static org.xmlmatchers.XmlMatchers.hasXPath; import static org.xmlmatchers.transform.XmlConverters.the; @Category(ReleaseTest.class) public class GatewayMultiFuncTest { private static Logger LOG = LoggerFactory.getLogger( GatewayMultiFuncTest.class ); private static Class DAT = GatewayMultiFuncTest.class; private static Enumeration<Appender> appenders; private static GatewayTestConfig config; private static DefaultGatewayServices services; private static GatewayServer gateway; private static int gatewayPort; private static String gatewayUrl; private static SimpleLdapDirectoryServer ldap; private static TcpTransport ldapTransport; private static Properties params; private static TopologyService topos; @BeforeClass public static void setupSuite() throws Exception { LOG_ENTER(); //appenders = NoOpAppender.setUp(); setupLdap(); setupGateway(); LOG_EXIT(); } @AfterClass public static void cleanupSuite() throws Exception { LOG_ENTER(); gateway.stop(); ldap.stop( true ); FileUtils.deleteQuietly( new File( config.getGatewayHomeDir() ) ); //NoOpAppender.tearDown( appenders ); LOG_EXIT(); } public static void setupLdap() throws Exception { URL usersUrl = TestUtils.getResourceUrl( DAT, "users.ldif" ); ldapTransport = new TcpTransport( 0 ); ldap = new SimpleLdapDirectoryServer( "dc=hadoop,dc=apache,dc=org", new File( usersUrl.toURI() ), ldapTransport ); ldap.start(); LOG.info( "LDAP port = " + ldapTransport.getAcceptor().getLocalAddress().getPort() ); } 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() ); URL svcsFileUrl = TestUtils.getResourceUrl( DAT, "services/readme.txt" ); File svcsFile = new File( svcsFileUrl.getFile() ); File svcsDir = svcsFile.getParentFile(); config.setGatewayServicesDir( svcsDir.getAbsolutePath() ); URL appsFileUrl = TestUtils.getResourceUrl( DAT, "applications/readme.txt" ); File appsFile = new File( appsFileUrl.getFile() ); File appsDir = appsFile.getParentFile(); config.setGatewayApplicationsDir( appsDir.getAbsolutePath() ); File topoDir = new File( config.getGatewayTopologyDir() ); topoDir.mkdirs(); File deployDir = new File( config.getGatewayDeploymentDir() ); deployDir.mkdirs(); startGatewayServer(); } public static void startGatewayServer() throws Exception { services = new DefaultGatewayServices(); Map<String,String> options = new HashMap<String,String>(); 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(); LOG.info( "Gateway port = " + gateway.getAddresses()[ 0 ].getPort() ); params = new Properties(); params.put( "LDAP_URL", "ldap://localhost:" + ldapTransport.getAcceptor().getLocalAddress().getPort() ); } @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) public void testDefaultJsonMimeTypeHandlingKnox678() throws Exception { LOG_ENTER(); MockServer mock = new MockServer( "REPEAT", true ); params = new Properties(); params.put( "LDAP_URL", "ldap://localhost:" + ldapTransport.getAcceptor().getLocalAddress().getPort() ); params.put( "MOCK_SERVER_PORT", mock.getPort() ); String topoStr = TestUtils.merge( DAT, "topologies/test-knox678-utf8-chars-topology.xml", params ); File topoFile = new File( config.getGatewayTopologyDir(), "knox678.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); String uname = "guest"; String pword = uname + "-password"; mock.expect().method( "GET" ) .respond().contentType( "application/json" ).contentLength( -1 ).content( "{\"msg\":\"H\u00eallo\"}", Charset.forName( "UTF-8" ) ); String json = given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "application/json; charset=UTF-8" ) .when().log().ifError().get( gatewayUrl + "/knox678/repeat" ).andReturn().asString(); assertThat( json, is("{\"msg\":\"H\u00eallo\"}") ); assertThat( mock.isEmpty(), is(true) ); mock.expect().method( "GET" ) .respond().contentType( "application/octet-stream" ).contentLength( -1 ).content( "H\u00eallo".getBytes() ); byte[] bytes = given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "application/octet-stream" ) .when().log().ifError().get( gatewayUrl + "/knox678/repeat" ).andReturn().asByteArray(); assertThat( bytes, is(equalTo("H\u00eallo".getBytes())) ); assertThat( mock.isEmpty(), is(true) ); mock.stop(); LOG_EXIT(); } @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) public void testPostWithContentTypeKnox681() throws Exception { LOG_ENTER(); MockServer mock = new MockServer( "REPEAT", true ); params = new Properties(); params.put( "MOCK_SERVER_PORT", mock.getPort() ); params.put( "LDAP_URL", "ldap://localhost:" + ldapTransport.getAcceptor().getLocalAddress().getPort() ); String topoStr = TestUtils.merge( DAT, "topologies/test-knox678-utf8-chars-topology.xml", params ); File topoFile = new File( config.getGatewayTopologyDir(), "knox681.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); mock .expect() .method( "PUT" ) .pathInfo( "/repeat-context/" ) .respond() .status( HttpStatus.SC_CREATED ) .content( "{\"name\":\"value\"}".getBytes() ) .contentLength( -1 ) .contentType( "application/json; charset=UTF-8" ) .header( "Location", gatewayUrl + "/knox681/repeat" ); String uname = "guest"; String pword = uname + "-password"; HttpHost targetHost = new HttpHost( "localhost", gatewayPort, "http" ); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( new AuthScope( targetHost.getHostName(), targetHost.getPort() ), new UsernamePasswordCredentials( uname, pword ) ); AuthCache authCache = new BasicAuthCache(); BasicScheme basicAuth = new BasicScheme(); authCache.put( targetHost, basicAuth ); HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider( credsProvider ); context.setAuthCache( authCache ); CloseableHttpClient client = HttpClients.createDefault(); HttpPut request = new HttpPut( gatewayUrl + "/knox681/repeat" ); request.addHeader( "X-XSRF-Header", "jksdhfkhdsf" ); request.addHeader( "Content-Type", "application/json" ); CloseableHttpResponse response = client.execute( request, context ); assertThat( response.getStatusLine().getStatusCode(), is( HttpStatus.SC_CREATED ) ); assertThat( response.getFirstHeader( "Location" ).getValue(), endsWith("/gateway/knox681/repeat" ) ); assertThat( response.getFirstHeader( "Content-Type" ).getValue(), is("application/json; charset=UTF-8") ); String body = new String( IOUtils.toByteArray( response.getEntity().getContent() ), Charset.forName( "UTF-8" ) ); assertThat( body, is( "{\"name\":\"value\"}" ) ); response.close(); client.close(); mock .expect() .method( "PUT" ) .pathInfo( "/repeat-context/" ) .respond() .status( HttpStatus.SC_CREATED ) .content( "<test-xml/>".getBytes() ) .contentType( "application/xml; charset=UTF-8" ) .header( "Location", gatewayUrl + "/knox681/repeat" ); client = HttpClients.createDefault(); request = new HttpPut( gatewayUrl + "/knox681/repeat" ); request.addHeader( "X-XSRF-Header", "jksdhfkhdsf" ); request.addHeader( "Content-Type", "application/xml" ); response = client.execute( request, context ); assertThat( response.getStatusLine().getStatusCode(), is( HttpStatus.SC_CREATED ) ); assertThat( response.getFirstHeader( "Location" ).getValue(), endsWith("/gateway/knox681/repeat" ) ); assertThat( response.getFirstHeader( "Content-Type" ).getValue(), is("application/xml; charset=UTF-8") ); body = new String( IOUtils.toByteArray( response.getEntity().getContent() ), Charset.forName( "UTF-8" ) ); assertThat( the(body), hasXPath( "/test-xml" ) ); response.close(); client.close(); mock.stop(); LOG_EXIT(); } @Test( timeout = TestUtils.MEDIUM_TIMEOUT ) public void testLdapSearchConfigEnhancementsKnox694() throws Exception { LOG_ENTER(); String topoStr; File topoFile; String adminUName = "uid=admin,ou=people,dc=hadoop,dc=apache,dc=org"; String adminPWord = "admin-password"; String uname = "people\\guest"; String pword = "guest-password"; String invalidPword = "invalid-guest-password"; params = new Properties(); params.put( "LDAP_URL", "ldap://localhost:" + ldapTransport.getAcceptor().getLocalAddress().getPort() ); params.put( "LDAP_SYSTEM_USERNAME", adminUName ); params.put( "LDAP_SYSTEM_PASSWORD", adminPWord ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-user-dn-template.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-1.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "text/plain" ) .body( is( "test-service-response" ) ) .when().log().ifError().get( gatewayUrl + "/knox694-1/test-service-path/test-resource-path" ); given() //.log().all() .auth().preemptive().basic( uname, invalidPword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-1/test-service-path/test-resource-path" ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-search-attribute.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-2.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "text/plain" ) .body( is( "test-service-response" ) ) .when().log().ifError().get( gatewayUrl + "/knox694-2/test-service-path/test-resource-path" ); given() //.log().all() .auth().preemptive().basic( uname, invalidPword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-2/test-service-path/test-resource-path" ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-search-filter.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-3.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "text/plain" ) .body( is( "test-service-response" ) ) .when().log().ifError().get( gatewayUrl + "/knox694-3/test-service-path/test-resource-path" ); given() //.log().all() .auth().preemptive().basic( uname, invalidPword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-3/test-service-path/test-resource-path" ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-search-scope-object.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-4.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "text/plain" ) .body( is( "test-service-response" ) ) .when().log().ifError().get( gatewayUrl + "/knox694-4/test-service-path/test-resource-path" ); given() //.log().all() .auth().preemptive().basic( uname, invalidPword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-4/test-service-path/test-resource-path" ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-search-scope-onelevel-positive.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-5.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_OK ) .contentType( "text/plain" ) .body( is( "test-service-response" ) ) .when().log().ifError().get( gatewayUrl + "/knox694-5/test-service-path/test-resource-path" ); given() //.log().all() .auth().preemptive().basic( uname, invalidPword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-5/test-service-path/test-resource-path" ); topoStr = TestUtils.merge( DAT, "topologies/test-knox694-principal-regex-search-scope-onelevel-negative.xml", params ); topoFile = new File( config.getGatewayTopologyDir(), "knox694-6.xml" ); FileUtils.writeStringToFile( topoFile, topoStr ); topos.reloadTopologies(); given() //.log().all() .auth().preemptive().basic( uname, pword ) .expect() //.log().all() .statusCode( HttpStatus.SC_UNAUTHORIZED ) .when().get( gatewayUrl + "/knox694-6/test-service-path/test-resource-path" ); LOG_EXIT(); } }