Skip to content
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
*.iml
target
src/test/java/io/*
.classpath
.project
.settings
5 changes: 3 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ 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 `<local>` value is an alias for `127.x.y.z` or `1::x` IP or `localhost`.

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=<local>` but you can disable the endpoints using `geronimo.metrics.jaxrs.activated=false`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,7 +50,24 @@ public void init() {
if ("<local>".equals(value)) {
return LOCAL_MATCHER;
}
return (Predicate<String>) value::equals;
return Optional.ofNullable(value)
.filter(range -> range.startsWith("[") && range.endsWith("]"))
.map(v -> ((Predicate<String>) 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<String>) value::equals);
}).orElse(singletonList(LOCAL_MATCHER));
acceptedRoles = config("geronimo.metrics.jaxrs.acceptedRoles", identity()).orElse(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;


import org.junit.Test;

public class SecurityValidatorTest {
Expand Down Expand Up @@ -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
Expand All @@ -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() {{
Expand Down Expand Up @@ -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);
}
Expand Down