refined PathResource alias handling
This commit is contained in:
parent
1873b306b3
commit
aaa2e5c6c1
|
@ -57,7 +57,7 @@ public class QuickStartConfiguration extends WebInfConfiguration
|
|||
Resource webApp = context.newResource(war);
|
||||
|
||||
// Accept aliases for WAR files
|
||||
if (webApp.getAlias() != null)
|
||||
if (webApp.isAlias())
|
||||
{
|
||||
LOG.debug(webApp + " anti-aliased to " + webApp.getAlias());
|
||||
webApp = context.newResource(webApp.getAlias());
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.nio.file.Path;
|
|||
import org.eclipse.jetty.server.handler.ContextHandler.AliasCheck;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.resource.PathResource;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
|
||||
|
||||
|
@ -41,56 +42,52 @@ public class AllowSymLinkAliasChecker implements AliasCheck
|
|||
private static final Logger LOG = Log.getLogger(AllowSymLinkAliasChecker.class);
|
||||
|
||||
@Override
|
||||
public boolean check(String path, Resource resource)
|
||||
public boolean check(String uri, Resource resource)
|
||||
{
|
||||
// Only support PathResource alias checking
|
||||
if (!(resource instanceof PathResource))
|
||||
return false;
|
||||
|
||||
PathResource pathResource = (PathResource)resource;
|
||||
|
||||
try
|
||||
{
|
||||
File file =resource.getFile();
|
||||
if (file==null)
|
||||
return false;
|
||||
Path path = pathResource.getPath();
|
||||
|
||||
// If the file exists
|
||||
if (file.exists())
|
||||
// is the file itself a symlink?
|
||||
if (Files.isSymbolicLink(path) && Files.isSameFile(path,pathResource.getAliasPath()))
|
||||
{
|
||||
// we can use the real path method to check the symlinks resolve to the alias
|
||||
URI real = file.toPath().toRealPath().toUri();
|
||||
if (real.equals(resource.getAlias()))
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Allow symlink {} --> {}",resource,pathResource.getAliasPath());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No, so let's check each element ourselves
|
||||
Path d = path.getRoot();
|
||||
for (Path e:path)
|
||||
{
|
||||
d=d.resolve(e);
|
||||
|
||||
while (Files.exists(d) && Files.isSymbolicLink(d))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Allow symlink {} --> {}",resource,real);
|
||||
return true;
|
||||
Path link=Files.readSymbolicLink(d);
|
||||
if (!link.isAbsolute())
|
||||
link=d.resolve(link);
|
||||
d=link;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (pathResource.getAliasPath().equals(d))
|
||||
{
|
||||
// file does not exists, so we have to walk the path and links ourselves.
|
||||
Path p = file.toPath().toAbsolutePath();
|
||||
File d = p.getRoot().toFile();
|
||||
for (Path e:p)
|
||||
{
|
||||
d=new File(d,e.toString());
|
||||
|
||||
while (d.exists() && Files.isSymbolicLink(d.toPath()))
|
||||
{
|
||||
Path link=Files.readSymbolicLink(d.toPath());
|
||||
if (!link.isAbsolute())
|
||||
link=link.resolve(d.toPath());
|
||||
d=link.toFile().getAbsoluteFile().getCanonicalFile();
|
||||
}
|
||||
}
|
||||
if (resource.getAlias().equals(d.toURI()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Allow symlink {} --> {}",resource,d);
|
||||
return true;
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Allow path symlink {} --> {}",resource,d);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOG.ignore(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1668,7 +1668,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
|||
public boolean checkAlias(String path, Resource resource)
|
||||
{
|
||||
// Is the resource aliased?
|
||||
if (resource.getAlias() != null)
|
||||
if (resource.isAlias())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aliased resource: " + resource + "~=" + resource.getAlias());
|
||||
|
|
|
@ -315,7 +315,7 @@ public class ResourceHandler extends HandlerWrapper
|
|||
{
|
||||
path=URIUtil.canonicalPath(path);
|
||||
Resource r = base.addPath(path);
|
||||
if (r!=null && r.getAlias()!=null && !_context.checkAlias(path, r))
|
||||
if (r!=null && r.isAlias() && !_context.checkAlias(path, r))
|
||||
return null;
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
|||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
@ -352,8 +353,8 @@ public class ContextHandlerGetResourceTest
|
|||
@Test
|
||||
public void testSymlinkKnown() throws Exception
|
||||
{
|
||||
if (!OS.IS_UNIX)
|
||||
return;
|
||||
Assume.assumeTrue(OS.IS_UNIX);
|
||||
|
||||
try
|
||||
{
|
||||
allowSymlinks.set(true);
|
||||
|
|
|
@ -50,36 +50,41 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
public class PathResource extends Resource
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(PathResource.class);
|
||||
private final static LinkOption NO_FOLLOW_OPTIONS[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
||||
private final static LinkOption NO_FOLLOW_LINKS[] = new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
||||
private final static LinkOption FOLLOW_LINKS[] = new LinkOption[] {};
|
||||
|
||||
private final Path path;
|
||||
private final URI alias;
|
||||
private final Path alias;
|
||||
private final URI uri;
|
||||
|
||||
private static final URI toAliasUri(final Path path)
|
||||
private static final Path checkAliasPath(final Path path)
|
||||
{
|
||||
Path abs = path;
|
||||
if (!abs.isAbsolute())
|
||||
{
|
||||
abs = path.toAbsolutePath();
|
||||
}
|
||||
URI providedUri = abs.toUri();
|
||||
|
||||
try
|
||||
{
|
||||
URI realUri = abs.toRealPath().toUri();
|
||||
if (!providedUri.equals(realUri))
|
||||
if (Files.isSymbolicLink(path))
|
||||
return Files.readSymbolicLink(path);
|
||||
if (Files.exists(path))
|
||||
{
|
||||
return realUri;
|
||||
Path real = abs.toRealPath(FOLLOW_LINKS);
|
||||
if (!abs.equals(real))
|
||||
return real;
|
||||
}
|
||||
}
|
||||
catch (NoSuchFileException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO: reevaluate severity level
|
||||
LOG.warn("bad alias ({}) for {}", e.getClass().getName(), e.getMessage());
|
||||
return abs;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -93,7 +98,7 @@ public class PathResource extends Resource
|
|||
{
|
||||
this.path = path.toAbsolutePath();
|
||||
this.uri = this.path.toUri();
|
||||
this.alias = toAliasUri(path);
|
||||
this.alias = checkAliasPath(path);
|
||||
}
|
||||
|
||||
public PathResource(URI uri) throws IOException
|
||||
|
@ -129,7 +134,7 @@ public class PathResource extends Resource
|
|||
|
||||
this.path = path.toAbsolutePath();
|
||||
this.uri = path.toUri();
|
||||
this.alias = toAliasUri(path);
|
||||
this.alias = checkAliasPath(path);
|
||||
}
|
||||
|
||||
public PathResource(URL url) throws IOException, URISyntaxException
|
||||
|
@ -211,7 +216,7 @@ public class PathResource extends Resource
|
|||
@Override
|
||||
public boolean exists()
|
||||
{
|
||||
return Files.exists(path,NO_FOLLOW_OPTIONS);
|
||||
return Files.exists(path,NO_FOLLOW_LINKS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -220,6 +225,11 @@ public class PathResource extends Resource
|
|||
return path.toFile();
|
||||
}
|
||||
|
||||
public Path getPath() throws IOException
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
|
@ -276,7 +286,7 @@ public class PathResource extends Resource
|
|||
@Override
|
||||
public boolean isDirectory()
|
||||
{
|
||||
return Files.isDirectory(path,NO_FOLLOW_OPTIONS);
|
||||
return Files.isDirectory(path,NO_FOLLOW_LINKS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -284,7 +294,7 @@ public class PathResource extends Resource
|
|||
{
|
||||
try
|
||||
{
|
||||
FileTime ft = Files.getLastModifiedTime(path,NO_FOLLOW_OPTIONS);
|
||||
FileTime ft = Files.getLastModifiedTime(path,NO_FOLLOW_LINKS);
|
||||
return ft.toMillis();
|
||||
}
|
||||
catch (IOException e)
|
||||
|
@ -309,10 +319,21 @@ public class PathResource extends Resource
|
|||
}
|
||||
|
||||
@Override
|
||||
public URI getAlias()
|
||||
public boolean isAlias()
|
||||
{
|
||||
return this.alias!=null;
|
||||
}
|
||||
|
||||
public Path getAliasPath()
|
||||
{
|
||||
return this.alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getAlias()
|
||||
{
|
||||
return this.alias==null?null:this.alias.toUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list()
|
||||
|
@ -354,7 +375,7 @@ public class PathResource extends Resource
|
|||
try
|
||||
{
|
||||
Path result = Files.move(path,destRes.path);
|
||||
return Files.exists(result,NO_FOLLOW_OPTIONS);
|
||||
return Files.exists(result,NO_FOLLOW_LINKS);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
|
|
|
@ -475,6 +475,12 @@ public abstract class Resource implements ResourceFactory, Closeable
|
|||
{
|
||||
_associate=o;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isAlias()
|
||||
{
|
||||
return getAlias()!=null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
|
|
|
@ -134,7 +134,7 @@ public class FileSystemResourceTest
|
|||
public boolean matches(Object item)
|
||||
{
|
||||
final Resource res = (Resource)item;
|
||||
return res.getAlias() == null;
|
||||
return !res.isAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -497,6 +497,48 @@ public class FileSystemResourceTest
|
|||
assertThat("file.alias", newResource(resBar.getFile()), isAliasFor(resFoo));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonExistantSymlink() throws Exception
|
||||
{
|
||||
File dir = testdir.getDir();
|
||||
|
||||
Path foo = new File(dir, "foo").toPath();
|
||||
Path bar = new File(dir, "bar").toPath();
|
||||
|
||||
try
|
||||
{
|
||||
Files.createSymbolicLink(bar,foo);
|
||||
}
|
||||
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 (Resource base = newResource(testdir.getDir()))
|
||||
{
|
||||
// FileResource does not pass this test!
|
||||
assumeFalse(base instanceof FileResource);
|
||||
|
||||
Resource resFoo = base.addPath("foo");
|
||||
Resource resBar = base.addPath("bar");
|
||||
|
||||
assertThat("resFoo.uri", resFoo.getURI(), is(foo.toUri()));
|
||||
|
||||
// Access to the same resource, but via a symlink means that they are not equivalent
|
||||
assertThat("foo.equals(bar)", resFoo.equals(resBar), is(false));
|
||||
|
||||
assertThat("resource.alias", resFoo, hasNoAlias());
|
||||
assertThat("resource.uri.alias", newResource(resFoo.getURI()), hasNoAlias());
|
||||
assertThat("resource.file.alias", newResource(resFoo.getFile()), hasNoAlias());
|
||||
|
||||
assertThat("alias", resBar, isAliasFor(resFoo));
|
||||
assertThat("uri.alias", newResource(resBar.getURI()), isAliasFor(resFoo));
|
||||
assertThat("file.alias", newResource(resBar.getFile()), isAliasFor(resFoo));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
|
|
|
@ -402,7 +402,7 @@ public class WebInfConfiguration extends AbstractConfiguration
|
|||
throw new IllegalStateException("No resourceBase or war set for context");
|
||||
|
||||
// Accept aliases for WAR files
|
||||
if (web_app.getAlias() != null)
|
||||
if (web_app.isAlias())
|
||||
{
|
||||
LOG.debug(web_app + " anti-aliased to " + web_app.getAlias());
|
||||
web_app = context.newResource(web_app.getAlias());
|
||||
|
|
Loading…
Reference in New Issue