diff --git a/src/integrationTest/java/org/opensearch/security/http/AnonymousAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/AnonymousAuthenticationTest.java index b1c13aeedc..bc874305de 100644 --- a/src/integrationTest/java/org/opensearch/security/http/AnonymousAuthenticationTest.java +++ b/src/integrationTest/java/org/opensearch/security/http/AnonymousAuthenticationTest.java @@ -9,14 +9,19 @@ */ package org.opensearch.security.http; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.List; import com.carrotsearch.randomizedtesting.RandomizedRunner; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.message.BasicHeader; import org.junit.ClassRule; import org.junit.Test; import org.junit.runner.RunWith; +import org.opensearch.security.user.User; import org.opensearch.test.framework.RolesMapping; import org.opensearch.test.framework.TestSecurityConfig; import org.opensearch.test.framework.cluster.ClusterManager; @@ -29,6 +34,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiAlphanumOfLength; @RunWith(RandomizedRunner.class) @ThreadLeakScope(ThreadLeakScope.Scope.NONE) @@ -80,7 +86,13 @@ public class AnonymousAuthenticationTest { public void shouldAuthenticate_positive_anonymousUser() { try (TestRestClient client = cluster.getRestClient()) { - TestRestClient.HttpResponse response = client.getAuthInfo(); + Header anonyAuthHeader = new BasicHeader( + "Authorization", + "Basic " + + Base64.getEncoder() + .encodeToString((User.ANONYMOUS.getName() + ":" + randomAsciiAlphanumOfLength(8)).getBytes(StandardCharsets.UTF_8)) + ); + TestRestClient.HttpResponse response = client.getAuthInfo(anonyAuthHeader); response.assertStatusCode(200); diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 3ab9a2afc9..0f16ef18fb 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -286,10 +286,6 @@ public boolean authenticate(final SecurityRequestChannel request) { if (ac == null) { // no credentials found in request - if (anonymousAuthEnabled) { - continue; - } - if (authDomain.isChallenge()) { final Optional restResponse = httpAuthenticator.reRequestAuthentication(request, null); if (restResponse.isPresent()) { @@ -309,6 +305,10 @@ public boolean authenticate(final SecurityRequestChannel request) { continue; } } else { + // credentials found for anonymous user. Skip looping over rest auth domains as this is a login request for anonymous user + if (anonymousAuthEnabled && ac.getUsername().equals(User.ANONYMOUS.getName())) { + break; + } org.apache.logging.log4j.ThreadContext.put("user", ac.getUsername()); if (!ac.isComplete()) { // credentials found in request but we need another client challenge @@ -386,17 +386,20 @@ public boolean authenticate(final SecurityRequestChannel request) { log.debug("User still not authenticated after checking {} auth domains", restAuthDomains.size()); } - if (authCredentials == null && anonymousAuthEnabled) { - final String tenant = resolveTenantFrom(request); - User anonymousUser = new User(User.ANONYMOUS.getName(), new HashSet(User.ANONYMOUS.getRoles()), null); - anonymousUser.setRequestedTenant(tenant); + if (anonymousAuthEnabled) { + assert authCredentials != null; + if (authCredentials.getUsername().equals(User.ANONYMOUS.getName())) { + final String tenant = resolveTenantFrom(request); + User anonymousUser = new User(User.ANONYMOUS.getName(), new HashSet(User.ANONYMOUS.getRoles()), null); + anonymousUser.setRequestedTenant(tenant); - threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, anonymousUser); - auditLog.logSucceededLogin(anonymousUser.getName(), false, null, request); - if (isDebugEnabled) { - log.debug("Anonymous User is authenticated"); + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, anonymousUser); + auditLog.logSucceededLogin(anonymousUser.getName(), false, null, request); + if (isDebugEnabled) { + log.debug("Anonymous User is authenticated"); + } + return true; } - return true; } Optional challengeResponse = Optional.empty(); diff --git a/src/test/java/org/opensearch/security/HttpIntegrationTests.java b/src/test/java/org/opensearch/security/HttpIntegrationTests.java index 3a437ea80a..1f1c45b320 100644 --- a/src/test/java/org/opensearch/security/HttpIntegrationTests.java +++ b/src/test/java/org/opensearch/security/HttpIntegrationTests.java @@ -31,6 +31,7 @@ import java.nio.file.Files; import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.NoHttpResponseException; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.http.HttpStatus; @@ -56,6 +57,7 @@ import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; +import org.opensearch.security.user.User; import static org.opensearch.security.DefaultObjectMapper.readTree; @@ -465,16 +467,17 @@ public void testHTTPAnon() throws Exception { setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_anon.yml"), Settings.EMPTY, true); RestHelper rh = nonSslRestHelper(); + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); - Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("", anonyAuthHeader).getStatusCode()); Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, rh.executeGetRequest("", encodeBasicHeader("worf", "wrong")).getStatusCode()); Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("", encodeBasicHeader("nagilum", "nagilum")).getStatusCode()); - HttpResponse resc = rh.executeGetRequest("_opendistro/_security/authinfo"); + HttpResponse resc = rh.executeGetRequest("_opendistro/_security/authinfo", anonyAuthHeader); Assert.assertTrue(resc.getBody().contains("opendistro_security_anonymous")); Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); - resc = rh.executeGetRequest("_opendistro/_security/authinfo?pretty=true"); + resc = rh.executeGetRequest("_opendistro/_security/authinfo?pretty=true", anonyAuthHeader); Assert.assertTrue(resc.getBody().contains("\"remote_address\" : \"")); // check pretty print Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); diff --git a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java index 7545822620..7b4e2c87f0 100644 --- a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java +++ b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java @@ -60,6 +60,7 @@ import org.opensearch.security.test.helper.file.FileHelper; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; +import org.opensearch.security.user.User; public class InitializationIntegrationTests extends SingleClusterTest { @@ -255,6 +256,7 @@ public void testConfigHotReload() throws Exception { Assert.assertEquals(clusterInfo.numNodes, cur.getNodes().size()); } + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); for (Iterator iterator = clusterInfo.httpAdresses.iterator(); iterator.hasNext();) { TransportAddress TransportAddress = iterator.next(); HttpResponse res = rh.executeRequest( @@ -265,7 +267,8 @@ public void testConfigHotReload() throws Exception { + TransportAddress.getPort() + "/" + "_opendistro/_security/authinfo?pretty=true" - ) + ), + anonyAuthHeader ); log.debug(res.getBody()); Assert.assertTrue(res.getBody().contains("role_host1")); diff --git a/src/test/java/org/opensearch/security/SecurityRolesTests.java b/src/test/java/org/opensearch/security/SecurityRolesTests.java index 0b4dd0b95b..b7e614e7fe 100644 --- a/src/test/java/org/opensearch/security/SecurityRolesTests.java +++ b/src/test/java/org/opensearch/security/SecurityRolesTests.java @@ -26,6 +26,7 @@ package org.opensearch.security; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.message.BasicHeader; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -37,6 +38,7 @@ import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; +import org.opensearch.security.user.User; public class SecurityRolesTests extends SingleClusterTest { @@ -51,8 +53,9 @@ public void testSecurityRolesAnon() throws Exception { ); RestHelper rh = nonSslRestHelper(); + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); - HttpResponse resc = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + HttpResponse resc = rh.executeGetRequest("_opendistro/_security/authinfo?pretty", anonyAuthHeader); Assert.assertTrue(resc.getBody().contains("anonymous")); Assert.assertFalse(resc.getBody().contains("xyz_sr")); Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); diff --git a/src/test/java/org/opensearch/security/multitenancy/test/MultitenancyTests.java b/src/test/java/org/opensearch/security/multitenancy/test/MultitenancyTests.java index 0a785d7b80..47b6fca567 100644 --- a/src/test/java/org/opensearch/security/multitenancy/test/MultitenancyTests.java +++ b/src/test/java/org/opensearch/security/multitenancy/test/MultitenancyTests.java @@ -35,6 +35,7 @@ import org.opensearch.security.test.SingleClusterTest; import org.opensearch.security.test.helper.rest.RestHelper; import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; +import org.opensearch.security.user.User; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -613,13 +614,15 @@ public void testMultitenancyAnonymousUser() throws Exception { new BasicHeader("securitytenant", anonymousTenant) ); + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); + /* The anonymous user has access to its tenant */ - res = rh.executeGetRequest(url, new BasicHeader("securitytenant", anonymousTenant)); + res = rh.executeGetRequest(url, new BasicHeader("securitytenant", anonymousTenant), anonyAuthHeader); Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); Assert.assertEquals(anonymousTenant, res.findValueInJson("_source.tenant")); /* No access to other tenants */ - res = rh.executeGetRequest(url, new BasicHeader("securitytenant", "human_resources")); + res = rh.executeGetRequest(url, new BasicHeader("securitytenant", "human_resources"), anonyAuthHeader); Assert.assertEquals(HttpStatus.SC_FORBIDDEN, res.getStatusCode()); } @@ -660,6 +663,7 @@ public void testMultitenancyUserReadWriteActions() throws Exception { @Test public void testMultitenancyAnonymousUserReadOnlyActions() throws Exception { setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_anonymous.yml"), Settings.EMPTY); + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); /* Create the tenant for the anonymous user to run the tests */ final String tenant = "anonymous_tenant"; @@ -671,12 +675,13 @@ public void testMultitenancyAnonymousUserReadOnlyActions() throws Exception { tenantExpectation.updateIndexStatusCode = HttpStatus.SC_FORBIDDEN; tenantExpectation.deleteIndexStatuCode = HttpStatus.SC_FORBIDDEN; - verifyTenantActions(nonSslRestHelper(), tenant, tenantExpectation, /* Anonymous user*/ null); + verifyTenantActions(nonSslRestHelper(), tenant, tenantExpectation, /* Anonymous user*/ anonyAuthHeader); } @Test public void testMultitenancyAnonymousUserWriteActionAllowed() throws Exception { setup(Settings.EMPTY, new DynamicSecurityConfig().setConfig("config_anonymous.yml"), Settings.EMPTY); + Header anonyAuthHeader = encodeBasicHeader(User.ANONYMOUS.getName(), randomAsciiAlphanumOfLength(8)); /* Create the tenant for the anonymous user to run the tests */ final String tenant = "opendistro_security_anonymous"; @@ -688,7 +693,7 @@ public void testMultitenancyAnonymousUserWriteActionAllowed() throws Exception { tenantExpectation.updateIndexStatusCode = HttpStatus.SC_OK; tenantExpectation.deleteIndexStatuCode = HttpStatus.SC_BAD_REQUEST; // tenant index cannot be deleted because its an alias - verifyTenantActions(nonSslRestHelper(), tenant, tenantExpectation, /* Anonymous user*/ null); + verifyTenantActions(nonSslRestHelper(), tenant, tenantExpectation, /* Anonymous user*/ anonyAuthHeader); } private static void verifyTenantActions(