diff --git a/.gitignore b/.gitignore index 218afde..15b39fb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ *.iml target src/test/java/io/* +.classpath +.project +.settings \ No newline at end of file diff --git a/README.adoc b/README.adoc index fc305ce..8f7783d 100644 --- a/README.adoc +++ b/README.adoc @@ -42,8 +42,9 @@ At least one must match to let the request pass, if none is set this validation Note that a request without a principal will lead to a HTTP 401 whereas a request with a principal but not the right role will issue a HTTP 403. The host validation will use the JAX-RS `UriInfo#getRequestUri`. -It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of roles. +It relies on the system property `geronimo.metrics.jaxrs.acceptedHosts` and it takes a comma separated list of hosts. At least one must match to let the request pass, if none is set this validation is ignored. +If the host value is an IP range (for example [10.10.10.0..10.10.10.255]) the match is true if the client ip is between the two ip address. The `` value is an alias for `127.x.y.z` or `1::x` IP or `localhost`. Configuration example: @@ -51,7 +52,7 @@ Configuration example: [source] ---- -Dgeronimo.metrics.jaxrs.acceptedRoles=ops \ --Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host +-Dgeronimo.metrics.jaxrs.acceptedHosts=my.remote.host,[10.10.10.0..10.10.10.255] ---- IMPORTANT: the default is `geronimo.metrics.jaxrs.acceptedHosts=` but you can disable the endpoints using `geronimo.metrics.jaxrs.activated=false`. diff --git a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java index d7d364f..800edff 100644 --- a/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java +++ b/geronimo-metrics-common/src/main/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidator.java @@ -21,6 +21,9 @@ import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -47,7 +50,24 @@ public void init() { if ("".equals(value)) { return LOCAL_MATCHER; } - return (Predicate) value::equals; + return Optional.ofNullable(value) + .filter(range -> range.startsWith("[") && range.endsWith("]")) + .map(v -> ((Predicate) ipToValidate -> { + return Optional.of(value) + .map(range -> range.subSequence(1, range.length() - 1).toString()) + .map(rangeWithoutBraces -> rangeWithoutBraces.split("\\.\\.")) + .filter(values -> values.length == 2) + .map(rangeArray -> { + try { + BigInteger addressMin = new BigInteger(InetAddress.getByName(rangeArray[0]).getAddress()); + BigInteger addressMax = new BigInteger(InetAddress.getByName(rangeArray[1]).getAddress()); + BigInteger addressToValidate = new BigInteger(InetAddress.getByName(ipToValidate).getAddress()); + return addressMin.max(addressToValidate).equals(addressMax.min(addressToValidate)); + } catch (UnknownHostException e) { + return false; + } + }).orElse(false); + })).orElse((Predicate) value::equals); }).orElse(singletonList(LOCAL_MATCHER)); acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null); } diff --git a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java index 63df1e0..b0c1ffe 100644 --- a/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java +++ b/geronimo-metrics-common/src/test/java/org/apache/geronimo/microprofile/metrics/common/jaxrs/SecurityValidatorTest.java @@ -27,7 +27,6 @@ import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; - import org.junit.Test; public class SecurityValidatorTest { @@ -95,6 +94,7 @@ public String getAuthenticationScheme() { } }; private static final UriInfo REMOTE = uri("http://geronimo.somewhere"); + private static final UriInfo REMOTE_IP = uri("http://10.10.10.1"); private static final UriInfo LOCALHOST = uri("http://localhost"); @Test @@ -104,6 +104,34 @@ public void localValid() { }}.checkSecurity(ANONYMOUS, LOCALHOST); } + @Test + public void remoteWithIpRangeValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; + } + }.checkSecurity(ANONYMOUS, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void remoteWithIpRangeInvalid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; + } + }.checkSecurity(ANONYMOUS, REMOTE_IP); + } + @Test(expected = WebApplicationException.class) public void remoteInvalid() { new SecurityValidator() {{ @@ -167,6 +195,62 @@ protected String config(final String key) { }.checkSecurity(ADMIN, REMOTE); } + @Test + public void roleAndIpRangeValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; + } + }.checkSecurity(ADMIN, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void roleAndIpRangeInvalid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; + } + }.checkSecurity(ADMIN, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void roleAnonymousAndIpRangeValid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[10.10.10.0..10.10.10.255]" : null; + } + }.checkSecurity(LOGGED_NO_ROLE, REMOTE_IP); + } + + @Test(expected = WebApplicationException.class) + public void roleAnonymousAndIpRangeInvalid() { + new SecurityValidator() { + { + init(); + } + + @Override + protected String config(final String key) { + return key.endsWith("acceptedRoles") ? "admin" : key.endsWith("acceptedHosts") ? "[20.10.10.0..20.10.10.255]" : null; + } + }.checkSecurity(LOGGED_NO_ROLE, REMOTE_IP); + } + private static UriInfo uri(final String request) { return new UriInfoMock(request); }