Fixed IncludeExclude combination logic

Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Greg Wilkins 2019-04-24 13:55:05 +02:00
parent 8b5f0211a8
commit feb0333844
6 changed files with 129 additions and 131 deletions

View File

@ -9,28 +9,28 @@
<Call name="include">
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.inetaccess.includedAddrs" default=""/></Arg>
<Arg><Property name="jetty.inetaccess.include" default=""/></Arg>
</Call>
</Arg>
</Call>
<Call name="exclude">
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.inetaccess.excludedAddrs" default=""/></Arg>
<Arg><Property name="jetty.inetaccess.exclude" default=""/></Arg>
</Call>
</Arg>
</Call>
<Call name="includeConnectorNames">
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.inetaccess.includedConnectors" default=""/></Arg>
<Arg><Property name="jetty.inetaccess.includeConnectorNames" default=""/></Arg>
</Call>
</Arg>
</Call>
<Call name="excludeConnectorNames">
<Arg>
<Call class="org.eclipse.jetty.util.StringUtil" name="csvSplit">
<Arg><Property name="jetty.inetaccess.excludedConnectors" default=""/></Arg>
<Arg><Property name="jetty.inetaccess.excludeConnectorNames" default=""/></Arg>
</Call>
</Arg>
</Call>

View File

@ -16,14 +16,14 @@ etc/jetty-inetaccess.xml
[ini-template]
## List of InetAddress patterns to include
#jetty.inetaccess.includedAddrs=127.0.0.1,127.0.0.2
#jetty.inetaccess.include=127.0.0.1,127.0.0.2
## List of InetAddress patterns to exclude
#jetty.inetaccess.excludedAddrs=127.0.0.1,127.0.0.2
#jetty.inetaccess.exclude=127.0.0.1,127.0.0.2
## List of Connector names to include
#jetty.inetaccess.includedConnectors=http
#jetty.inetaccess.includeConnectorNames=http
## List of Connector names to exclude
#jetty.inetaccess.excludedConnectors=tls
#jetty.inetaccess.excludeConnectorNames=tls

View File

@ -21,7 +21,6 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -35,7 +34,6 @@ import org.eclipse.jetty.util.IncludeExclude;
import org.eclipse.jetty.util.IncludeExcludeSet;
import org.eclipse.jetty.util.InetAddressSet;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -57,7 +55,7 @@ public class InetAccessHandler extends HandlerWrapper
private static final Logger LOG = Log.getLogger(InetAccessHandler.class);
private final IncludeExcludeSet<String, InetAddress> _addrs = new IncludeExcludeSet<>(InetAddressSet.class);
private final IncludeExclude<String> _connectorNames = new IncludeExclude<>();
private final IncludeExclude<String> _names = new IncludeExclude<>();
/**
* Clears all the includes, excludes, included connector names and excluded
@ -66,7 +64,7 @@ public class InetAccessHandler extends HandlerWrapper
public void clear()
{
_addrs.clear();
_connectorNames.clear();
_names.clear();
}
/**
@ -120,7 +118,7 @@ public class InetAccessHandler extends HandlerWrapper
*/
public void includeConnectorName(String name)
{
_connectorNames.include(name);
_names.include(name);
}
/**
@ -130,7 +128,7 @@ public class InetAccessHandler extends HandlerWrapper
*/
public void excludeConnectorName(String name)
{
_connectorNames.exclude(name);
_names.exclude(name);
}
/**
@ -140,7 +138,7 @@ public class InetAccessHandler extends HandlerWrapper
*/
public void includeConnectorNames(String... names)
{
_connectorNames.include(names);
_names.include(names);
}
/**
@ -150,7 +148,7 @@ public class InetAccessHandler extends HandlerWrapper
*/
public void excludeConnectorNames(String... names)
{
_connectorNames.exclude(names);
_names.exclude(names);
}
/**
@ -183,37 +181,21 @@ public class InetAccessHandler extends HandlerWrapper
/**
* Checks if specified address and request are allowed by current InetAddress rules.
*
* @param address the inetAddress to check
* @param addr the inetAddress to check
* @param baseRequest the base request to check
* @param request the HttpServletRequest request to check
* @return true if inetAddress and request are allowed
*/
protected boolean isAllowed(InetAddress address, Request baseRequest, HttpServletRequest request)
protected boolean isAllowed(InetAddress addr, Request baseRequest, HttpServletRequest request)
{
String connectorName = baseRequest.getHttpChannel().getConnector().getName();
boolean allowed = !isMatchingConnectorName(connectorName) || _addrs.test(address);
String name = baseRequest.getHttpChannel().getConnector().getName();
if (LOG.isDebugEnabled())
LOG.debug("{} {} {} for {}", this, allowed ? "allowed" : "denied", address, request);
return allowed;
}
/**
* Checks if this is a connector name that applies to this access handler.
*
* @return true if connector name is applicable given connectorNames property
*/
protected boolean isMatchingConnectorName(String connectorName)
{
boolean hasConnectorNames = !_connectorNames.getIncluded().isEmpty();
if (connectorName == null)
{
return !hasConnectorNames;
Boolean allowedByName = _names.isIncludedAndNotExcluded(name);
Boolean allowedByAddr = _addrs.isIncludedAndNotExcluded(addr);
LOG.debug("{} allowedByName={} allowedByAddr={} for {}/{}", this, allowedByName, allowedByAddr, addr, request);
}
if (hasConnectorNames && !_connectorNames.getIncluded().contains(connectorName))
{
return false;
}
return !_connectorNames.getExcluded().contains(connectorName);
return IncludeExclude.and(_addrs, addr, _names, baseRequest.getHttpChannel().getConnector()::getName);
}
@Override
@ -222,7 +204,7 @@ public class InetAccessHandler extends HandlerWrapper
dumpObjects(out, indent,
Dumpable.labelled("included", _addrs.getIncluded()),
Dumpable.labelled("excluded", _addrs.getExcluded()),
Dumpable.labelled("includedConnectorNames", _connectorNames.getIncluded()),
Dumpable.labelled("excludedConnectorNames", _connectorNames.getExcluded()));
Dumpable.labelled("includedConnectorNames", _names.getIncluded()),
Dumpable.labelled("excludedConnectorNames", _names.getExcluded()));
}
}

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.server.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
@ -51,6 +48,9 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class InetAccessHandlerTest
{
private static Server _server;
@ -91,7 +91,7 @@ public class InetAccessHandlerTest
/* ------------------------------------------------------------ */
@ParameterizedTest
@MethodSource("data")
public void testHandler(String include, String exclude, String includeConnectors, String excludeConnectors, String host, String uri, String code)
public void testHandler(String include, String exclude, String includeConnectors, String excludeConnectors, String code)
throws Exception
{
_handler.clear();
@ -124,7 +124,7 @@ public class InetAccessHandlerTest
}
}
String request = "GET " + uri + " HTTP/1.1\n" + "Host: " + host + "\n\n";
String request = "GET /path HTTP/1.1\n" + "Host: 127.0.0.1\n\n";
Socket socket = new Socket("127.0.0.1", _connector.getLocalPort());
socket.setSoTimeout(5000);
try
@ -137,7 +137,7 @@ public class InetAccessHandlerTest
Response response = readResponse(input);
Object[] params = new Object[]
{ "Request WBHUC", include, exclude, includeConnectors, excludeConnectors, host, uri, code, "Response", response.getCode() };
{ "Request WBHUC", include, exclude, includeConnectors, excludeConnectors, code, "Response", response.getCode() };
assertEquals(code, response.getCode(), Arrays.deepToString(params));
} finally
{
@ -256,55 +256,34 @@ public class InetAccessHandlerTest
Object[][] data = new Object[][]
{
// Empty lists
{ "", "", "", "", "127.0.0.1", "/", "200" },
{ "", "", "", "", "127.0.0.1", "/dump/info", "200" },
{ "", "", "", "", "200" },
// test simple filters
{ "127.0.0.1", "", "", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1", "", "", "", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1", "", "", "", "127.0.0.1", "/", "403" },
{ "192.0.0.1", "", "", "", "127.0.0.1", "/dump/info", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "", "127.0.0.1", "/", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "", "127.0.0.1", "/dump/info", "403" },
{ "127.0.0.1", "", "", "", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "", "200" },
{ "192.0.0.1", "", "", "", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "", "403" },
// test connector name filters
{ "127.0.0.1", "", "http", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1", "", "http", "", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1-127.0.0.254", "", "http", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1-127.0.0.254", "", "http", "", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1", "", "http", "", "127.0.0.1", "/", "403" },
{ "192.0.0.1", "", "http", "", "127.0.0.1", "/dump/info", "403" },
{ "192.0.0.1-192.0.0.254", "", "http", "", "127.0.0.1", "/", "403" },
{ "192.0.0.1-192.0.0.254", "", "http", "", "127.0.0.1", "/dump/info", "403" },
{ "127.0.0.1", "", "http", "", "200" },
{ "127.0.0.1-127.0.0.254", "", "http", "", "200" },
{ "192.0.0.1", "", "http", "", "403" },
{ "192.0.0.1-192.0.0.254", "", "http", "", "403" },
{ "127.0.0.1", "", "nothttp", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1", "", "nothttp", "", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1-127.0.0.254", "", "nothttp", "", "127.0.0.1", "/", "200" },
{ "127.0.0.1-127.0.0.254", "", "nothttp", "", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1", "", "nothttp", "", "127.0.0.1", "/", "200" },
{ "192.0.0.1", "", "nothttp", "", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1-192.0.0.254", "", "nothttp", "", "127.0.0.1", "/", "200" },
{ "192.0.0.1-192.0.0.254", "", "nothttp", "", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1", "", "nothttp", "", "403" },
{ "127.0.0.1-127.0.0.254", "", "nothttp", "", "403" },
{ "192.0.0.1", "", "nothttp", "", "403" },
{ "192.0.0.1-192.0.0.254", "", "nothttp", "", "403" },
{ "127.0.0.1", "", "", "http", "127.0.0.1", "/", "200" },
{ "127.0.0.1", "", "", "http", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "http", "127.0.0.1", "/", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "http", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1", "", "", "http", "127.0.0.1", "/", "200" },
{ "192.0.0.1", "", "", "http", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1-192.0.0.254", "", "", "http", "127.0.0.1", "/", "200" },
{ "192.0.0.1-192.0.0.254", "", "", "http", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1", "", "", "http", "403" },
{ "127.0.0.1-127.0.0.254", "", "", "http", "403" },
{ "192.0.0.1", "", "", "http", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "http", "403" },
{ "127.0.0.1", "", "", "nothttp", "127.0.0.1", "/", "200" },
{ "127.0.0.1", "", "", "nothttp", "127.0.0.1", "/dump/info", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "nothttp", "127.0.0.1", "/", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "nothttp", "127.0.0.1", "/dump/info", "200" },
{ "192.0.0.1", "", "", "nothttp", "127.0.0.1", "/", "403" },
{ "192.0.0.1", "", "", "nothttp", "127.0.0.1", "/dump/info", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "nothttp", "127.0.0.1", "/", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "nothttp", "127.0.0.1", "/dump/info", "403" },
{ "127.0.0.1", "", "", "nothttp", "200" },
{ "127.0.0.1-127.0.0.254", "", "", "nothttp", "200" },
{ "192.0.0.1", "", "", "nothttp", "403" },
{ "192.0.0.1-192.0.0.254", "", "", "nothttp", "403" },
};
return Arrays.asList(data).stream().map(Arguments::of);

View File

@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Supplier;
/** Utility class to maintain a set of inclusions and exclusions.
@ -168,14 +169,14 @@ public class IncludeExcludeSet<T,P> implements Predicate<P>
/**
* Test Included and not Excluded
* @param t The item to test
* @return Boolean.TRUE if t is included, Boolean.FALSE if t is excluded and null if neither
* @param item The item to test
* @return Boolean.TRUE if item is included, Boolean.FALSE if item is excluded and null if neither
*/
public Boolean isIncludedAndNotExcluded(P t)
public Boolean isIncludedAndNotExcluded(P item)
{
if (_excludePredicate.test(t))
if (_excludePredicate.test(item))
return Boolean.FALSE;
if (_includePredicate.test(t))
if (_includePredicate.test(item))
return Boolean.TRUE;
return null;
@ -210,11 +211,72 @@ public class IncludeExcludeSet<T,P> implements Predicate<P>
@Override
public String toString()
{
return String.format("%s@%x{i=%s,ip=%s,e=%s,ep=%s}",this.getClass().getSimpleName(),hashCode(),_includes,_includePredicate,_excludes,_excludePredicate);
return String.format("%s@%x{i=%s,ip=%s,e=%s,ep=%s}",this.getClass().getSimpleName(),hashCode(),
_includes,
_includePredicate==_includes?"SELF":_includePredicate,
_excludes,
_excludePredicate==_excludes?"SELF":_excludePredicate);
}
public boolean isEmpty()
{
return _includes.isEmpty() && _excludes.isEmpty();
}
/**
* Combine the results of two {@link IncludeExcludeSet}s with an "and" operation. The items must be
* included by xSet AND ySet, and excluded from neither. If a sets inclusions are empty, then all items
* are considered to be included.
* @param xSet The set that param x is tested against
* @param x An item to test against xSet
* @param ySet The set that param y is tested against
* @param y A supplier of an item to test against ySet, that is executed only of x is not excluded from xSet
* @param <XS> The type of xSet items
* @param <XP> The type of xSet predicates
* @param <YS> The type of ySet items
* @param <YP> The type of ySet predicates
* @return True only if the items are included and not excluded from their respected sets
*/
public static <XS, XP, YS, YP> boolean and(IncludeExcludeSet<XS, XP> xSet, XP x, IncludeExcludeSet<YS,YP> ySet, Supplier<YP> y)
{
Boolean xb = xSet.isIncludedAndNotExcluded(x);
if (Boolean.FALSE==xb || (xb==null && xSet.hasIncludes()))
return false;
Boolean yb = ySet.isIncludedAndNotExcluded(y.get());
if (Boolean.FALSE==yb || (yb==null && ySet.hasIncludes()))
return false;
return true;
}
/**
* Combine the results of two {@link IncludeExcludeSet}s with an "or" operation. The items must be
* included by xSet OR ySet, and excluded from neither. If a sets inclusions are empty, then all items
* are considered to be included.
* @param xSet The set that param x is tested against
* @param x An item to test against xSet
* @param ySet The set that param y is tested against
* @param ySupplier A supplier of an item to test against ySet, that is executed only of x is not excluded from xSet
* @param <XS> The type of xSet items
* @param <XP> The type of xSet predicates
* @param <YS> The type of ySet items
* @param <YP> The type of ySet predicates
* @return True only if the items are included and not excluded from their respected sets
*/
public static <XS, XP, YS, YP> boolean or(IncludeExcludeSet<XS, XP> xSet, XP x, IncludeExcludeSet<YS,YP> ySet, Supplier<YP> ySupplier)
{
Boolean xb = xSet.isIncludedAndNotExcluded(x);
if (Boolean.FALSE==xb)
return false;
YP y = ySupplier.get();
Boolean yb = ySet.isIncludedAndNotExcluded(y);
if (Boolean.FALSE==yb)
return false;
return Boolean.TRUE.equals(xb)
|| Boolean.TRUE.equals(yb)
|| !xSet.hasIncludes() && !ySet.hasIncludes();
}
}

View File

@ -22,6 +22,7 @@ package org.eclipse.jetty.webapp;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -685,21 +686,7 @@ public class ClasspathPattern extends AbstractSet<String>
{
try
{
Boolean byName = _patterns.isIncludedAndNotExcluded(clazz.getName());
if (Boolean.FALSE.equals(byName))
return byName; // Already excluded so no need to check location.
URI location = TypeUtil.getLocationOfClass(clazz);
Boolean byLocation = location == null ? null
: _locations.isIncludedAndNotExcluded(location);
if (LOG.isDebugEnabled())
LOG.debug("match {} from {} byName={} byLocation={} in {}",clazz,location,byName,byLocation,this);
// Combine the tri-state match of both IncludeExclude Sets
boolean included = Boolean.TRUE.equals(byName) || Boolean.TRUE.equals(byLocation)
|| (byName==null && !_patterns.hasIncludes() && byLocation==null && !_locations.hasIncludes());
boolean excluded = Boolean.FALSE.equals(byName) || Boolean.FALSE.equals(byLocation);
return included && !excluded;
return IncludeExcludeSet.or(_patterns, clazz.getName(), _locations, ()->TypeUtil.getLocationOfClass(clazz));
}
catch (Exception e)
{
@ -717,30 +704,18 @@ public class ClasspathPattern extends AbstractSet<String>
// Treat path elements as packages for name matching
name=name.replace("/",".");
Boolean byName = _patterns.isIncludedAndNotExcluded(name);
if (Boolean.FALSE.equals(byName))
return byName; // Already excluded so no need to check location.
// Try to find a file path for location matching
Boolean byLocation = null;
try
return IncludeExcludeSet.or(_patterns, name, _locations, ()->
{
URI jarUri = URIUtil.getJarSource(url.toURI());
if ("file".equalsIgnoreCase(jarUri.getScheme()))
try
{
byLocation = _locations.isIncludedAndNotExcluded(jarUri);
return URIUtil.getJarSource(url.toURI());
}
}
catch(Exception e)
{
LOG.ignore(e);
}
// Combine the tri-state match of both IncludeExclude Sets
boolean included = Boolean.TRUE.equals(byName) || Boolean.TRUE.equals(byLocation)
|| (byName==null && !_patterns.hasIncludes() && byLocation==null && !_locations.hasIncludes());
boolean excluded = Boolean.FALSE.equals(byName) || Boolean.FALSE.equals(byLocation);
return included && !excluded;
catch (URISyntaxException e)
{
LOG.ignore(e);
return null;
}
});
}
}