| // |
| // ======================================================================== |
| // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. |
| // ------------------------------------------------------------------------ |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v1.0 |
| // and Apache License v2.0 which accompanies this distribution. |
| // |
| // The Eclipse Public License is available at |
| // http://www.eclipse.org/legal/epl-v10.html |
| // |
| // The Apache License v2.0 is available at |
| // http://www.opensource.org/licenses/apache2.0.php |
| // |
| // You may elect to redistribute this code under either of these licenses. |
| // ======================================================================== |
| // |
| |
| package org.eclipse.jetty.server.handler; |
| |
| import static org.hamcrest.Matchers.containsString; |
| import static org.hamcrest.Matchers.startsWith; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assume.assumeNoException; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.FileSystemException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.jetty.http.HttpTester; |
| import org.eclipse.jetty.server.LocalConnector; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.toolchain.test.FS; |
| import org.eclipse.jetty.toolchain.test.MavenTestingUtils; |
| import org.eclipse.jetty.util.BufferUtil; |
| import org.eclipse.jetty.util.resource.PathResource; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.Parameterized; |
| |
| @RunWith(Parameterized.class) |
| public class AllowSymLinkAliasCheckerTest |
| { |
| @Parameterized.Parameters(name = "{0}") |
| public static List<Object[]> params() |
| { |
| List<Object[]> data = new ArrayList<>(); |
| |
| String dirs[] = {"/testdir/", "/testdirlnk/", "/testdirprefixlnk/", "/testdirsuffixlnk/", |
| "/testdirwraplnk/"}; |
| |
| for (String dirname : dirs) |
| { |
| data.add(new Object[]{dirname, 200, "text/html", "Directory: " + dirname}); |
| data.add(new Object[]{dirname + "testfile.txt", 200, "text/plain", "Hello TestFile"}); |
| data.add(new Object[]{dirname + "testfilelnk.txt", 200, "text/plain", "Hello TestFile"}); |
| data.add(new Object[]{dirname + "testfileprefixlnk.txt", 200, "text/plain", "Hello TestFile"}); |
| } |
| |
| return data; |
| } |
| |
| private Server server; |
| private LocalConnector localConnector; |
| private Path rootPath; |
| |
| @Before |
| public void setup() throws Exception |
| { |
| setupRoot(); |
| setupServer(); |
| } |
| |
| @After |
| public void teardown() throws Exception |
| { |
| if( server != null ) |
| { |
| server.stop(); |
| } |
| } |
| |
| private void setupRoot() throws IOException |
| { |
| rootPath = MavenTestingUtils.getTargetTestingPath(AllowSymLinkAliasCheckerTest.class.getSimpleName()); |
| FS.ensureEmpty(rootPath); |
| |
| Path testdir = rootPath.resolve("testdir"); |
| FS.ensureDirExists(testdir); |
| |
| try |
| { |
| // If we used testdir (Path) from above, these symlinks |
| // would point to an absolute path. |
| |
| // Create a relative symlink testdirlnk -> testdir |
| Files.createSymbolicLink(rootPath.resolve("testdirlnk"), new File("testdir").toPath()); |
| // Create a relative symlink testdirprefixlnk -> ./testdir |
| Files.createSymbolicLink(rootPath.resolve("testdirprefixlnk"), new File("./testdir").toPath()); |
| // Create a relative symlink testdirsuffixlnk -> testdir/ |
| Files.createSymbolicLink(rootPath.resolve("testdirsuffixlnk"), new File("testdir/").toPath()); |
| // Create a relative symlink testdirwraplnk -> ./testdir/ |
| Files.createSymbolicLink(rootPath.resolve("testdirwraplnk"), new File("./testdir/").toPath()); |
| } |
| catch (UnsupportedOperationException | FileSystemException e) |
| { |
| // If unable to create symlink, no point testing the rest. |
| // This is the path that Microsoft Windows takes. |
| assumeNoException(e); |
| } |
| |
| Path testfileTxt = testdir.resolve("testfile.txt"); |
| Files.createFile(testfileTxt); |
| try (OutputStream out = Files.newOutputStream(testfileTxt)) |
| { |
| out.write("Hello TestFile".getBytes(StandardCharsets.UTF_8)); |
| } |
| |
| try |
| { |
| Path testfileTxtLnk = testdir.resolve("testfilelnk.txt"); |
| // Create a relative symlink testfilelnk.txt -> testfile.txt |
| // If we used testfileTxt (Path) from above, this symlink |
| // would point to an absolute path. |
| Files.createSymbolicLink(testfileTxtLnk, new File("testfile.txt").toPath()); |
| } |
| catch (UnsupportedOperationException | FileSystemException e) |
| { |
| // If unable to create symlink, no point testing the rest. |
| // This is the path that Microsoft Windows takes. |
| assumeNoException(e); |
| } |
| |
| try |
| { |
| Path testfilePrefixTxtLnk = testdir.resolve("testfileprefixlnk.txt"); |
| // Create a relative symlink testfileprefixlnk.txt -> ./testfile.txt |
| // If we used testfileTxt (Path) from above, this symlink |
| // would point to an absolute path. |
| Files.createSymbolicLink(testfilePrefixTxtLnk, new File("./testfile.txt").toPath()); |
| } |
| catch (UnsupportedOperationException | FileSystemException e) |
| { |
| // If unable to create symlink, no point testing the rest. |
| // This is the path that Microsoft Windows takes. |
| assumeNoException(e); |
| } |
| } |
| |
| private void setupServer() throws Exception |
| { |
| // Setup server |
| server = new Server(); |
| localConnector = new LocalConnector(server); |
| server.addConnector(localConnector); |
| |
| ResourceHandler fileResourceHandler = new ResourceHandler(); |
| fileResourceHandler.setDirectoriesListed(true); |
| fileResourceHandler.setWelcomeFiles(new String[]{"index.html"}); |
| fileResourceHandler.setEtags(true); |
| |
| ContextHandler fileResourceContext = new ContextHandler(); |
| fileResourceContext.setContextPath("/"); |
| fileResourceContext.setAllowNullPathInfo(true); |
| fileResourceContext.setHandler(fileResourceHandler); |
| fileResourceContext.setBaseResource(new PathResource(rootPath)); |
| |
| fileResourceContext.clearAliasChecks(); |
| fileResourceContext.addAliasCheck(new AllowSymLinkAliasChecker()); |
| |
| server.setHandler(fileResourceContext); |
| server.start(); |
| } |
| |
| @Parameterized.Parameter(0) |
| public String requestURI; |
| @Parameterized.Parameter(1) |
| public int expectedResponseStatus; |
| @Parameterized.Parameter(2) |
| public String expectedResponseContentType; |
| @Parameterized.Parameter(3) |
| public String expectedResponseContentContains; |
| |
| public AllowSymLinkAliasCheckerTest() |
| { |
| } |
| |
| @Test(timeout = 5000) |
| public void testAccess() throws Exception |
| { |
| HttpTester.Request request = HttpTester.newRequest(); |
| |
| request.setMethod("GET"); |
| request.setHeader("Host", "tester"); |
| request.setURI(requestURI); |
| |
| String responseString = localConnector.getResponse(BufferUtil.toString(request.generate())); |
| assertThat("Response status code", responseString, startsWith("HTTP/1.1 " + expectedResponseStatus + " ")); |
| assertThat("Response Content-Type", responseString, containsString("\nContent-Type: " + expectedResponseContentType)); |
| assertThat("Response", responseString, containsString(expectedResponseContentContains)); |
| } |
| } |