diff --git a/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java b/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java index ade540f85c..9b39320e6c 100644 --- a/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java +++ b/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java @@ -138,6 +138,13 @@ public void testWhoAmIWithoutGetPermissions() { } } + @Test + public void testWhoAmIWithoutGetPermissionsWithoutLeadingSlashInPath() { + try (TestRestClient client = cluster.getRestClient(WHO_AM_I_NO_PERM)) { + assertThat(client.getWithoutLeadingSlash(WHOAMI_PROTECTED_ENDPOINT).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + } + } + @Test public void testWhoAmIPost() { try (TestRestClient client = cluster.getRestClient(WHO_AM_I)) { diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java index 90b86ecb25..cf28f7d67d 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/TestRestClient.java @@ -32,6 +32,7 @@ import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -111,6 +112,13 @@ public HttpResponse get(String path, Header... headers) { return executeRequest(new HttpGet(getHttpServerUri() + "/" + path), headers); } + public HttpResponse getWithoutLeadingSlash(String path, Header... headers) { + URI uri = URI.create(getHttpServerUri()); + uri = uri.resolve(path); + HttpUriRequest req = new HttpGet(uri); + return executeRequest(req, headers); + } + public HttpResponse getAuthInfo(Header... headers) { return executeRequest(new HttpGet(getHttpServerUri() + "/_opendistro/_security/authinfo?pretty"), headers); } diff --git a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java index c9d10ee2fa..12dd68d1f8 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java @@ -333,6 +333,9 @@ public void onAllowlistingSettingChanged(AllowlistingSettings allowlistingSettin * @return true if the request path matches the route */ private boolean restPathMatches(String requestPath, String handlerPath) { + // Trim leading and trailing slashes + requestPath = requestPath.replaceAll("^/+", "").replaceAll("/+$", ""); + handlerPath = handlerPath.replaceAll("^/+", "").replaceAll("/+$", ""); // Check exact match if (handlerPath.equals(requestPath)) { return true; diff --git a/src/test/java/org/opensearch/security/filter/RestPathMatchesTests.java b/src/test/java/org/opensearch/security/filter/RestPathMatchesTests.java index fd686bf857..0c094033f3 100644 --- a/src/test/java/org/opensearch/security/filter/RestPathMatchesTests.java +++ b/src/test/java/org/opensearch/security/filter/RestPathMatchesTests.java @@ -72,16 +72,37 @@ public void testRequestPathWithNamedParam() throws InvocationTargetException, Il } @Test - public void testRequestPathMismatch() throws InvocationTargetException, IllegalAccessException { - String requestPath = "_plugins/security/api/x/y"; - String handlerPath = "_plugins/security/api/z/y"; + public void testMatchWithLeadingSlashDifference() throws InvocationTargetException, IllegalAccessException { + String requestPath = "api/v1/resource"; + String handlerPath = "/api/v1/resource"; + assertTrue((Boolean) restPathMatches.invoke(securityRestFilter, requestPath, handlerPath)); + } + + @Test + public void testMatchWithTrailingSlashDifference() throws InvocationTargetException, IllegalAccessException { + String requestPath = "/api/v1/resource/"; + String handlerPath = "/api/v1/resource"; + assertTrue((Boolean) restPathMatches.invoke(securityRestFilter, requestPath, handlerPath)); + } + + @Test + public void testPathsMatchWithMultipleNamedParameters() throws InvocationTargetException, IllegalAccessException { + String requestPath = "/api/v1/resource/123/details"; + String handlerPath = "/api/v1/resource/{id}/details"; + assertTrue((Boolean) restPathMatches.invoke(securityRestFilter, requestPath, handlerPath)); + } + + @Test + public void testPathsDoNotMatchWithNonMatchingNamedParameterSegment() throws InvocationTargetException, IllegalAccessException { + String requestPath = "/api/v1/resource/123/details"; + String handlerPath = "/api/v1/resource/{id}/summary"; assertFalse((Boolean) restPathMatches.invoke(securityRestFilter, requestPath, handlerPath)); } @Test - public void testRequestPathWithExtraSegments() throws InvocationTargetException, IllegalAccessException { - String requestPath = "_plugins/security/api/x/y/z"; - String handlerPath = "_plugins/security/api/x/y"; + public void testDifferentSegmentCount() throws InvocationTargetException, IllegalAccessException { + String requestPath = "/api/v1/resource/123/extra"; + String handlerPath = "/api/v1/resource/{id}"; assertFalse((Boolean) restPathMatches.invoke(securityRestFilter, requestPath, handlerPath)); } } diff --git a/src/test/java/org/opensearch/security/filter/SecurityRestFilterUnitTests.java b/src/test/java/org/opensearch/security/filter/SecurityRestFilterUnitTests.java index 1727fddcc3..862aec17e4 100644 --- a/src/test/java/org/opensearch/security/filter/SecurityRestFilterUnitTests.java +++ b/src/test/java/org/opensearch/security/filter/SecurityRestFilterUnitTests.java @@ -100,4 +100,7 @@ public void testDoesCallDelegateOnSuccessfulAuthorization() throws Exception { verify(testRestHandlerSpy).handleRequest(any(), any(), any()); } + + // unit tests for restPathMatches are in RestPathMatchesTests.java + }