From db24b2a7b0f5f5d6ceb3163153aad9a34da7d8cc Mon Sep 17 00:00:00 2001 From: Lukasz Soszynski Date: Wed, 2 Nov 2022 11:31:00 +0100 Subject: [PATCH] Tests for extended proxy authentication. Signed-off-by: Lukasz Soszynski --- .../java/org/opensearch/security/Song.java | 1 - .../http/CommonProxyAuthenticationTests.java | 255 ++++++++++++++++++ .../http/ExtendedProxyAuthenticationTest.java | 246 +++++++++++++++++ .../http/ProxyAuthenticationTest.java | 217 +++------------ .../cluster/OpenSearchClientProvider.java | 20 +- 5 files changed, 541 insertions(+), 198 deletions(-) create mode 100644 src/integrationTest/java/org/opensearch/security/http/CommonProxyAuthenticationTests.java create mode 100644 src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java diff --git a/src/integrationTest/java/org/opensearch/security/Song.java b/src/integrationTest/java/org/opensearch/security/Song.java index cf585e5dc7..14b1ce9131 100644 --- a/src/integrationTest/java/org/opensearch/security/Song.java +++ b/src/integrationTest/java/org/opensearch/security/Song.java @@ -18,7 +18,6 @@ public class Song { public static final String FIELD_ARTIST = "artist"; public static final String FIELD_LYRICS = "lyrics"; public static final String FIELD_STARS = "stars"; - public static final String FIELD_GENRE = "genre"; public static final String ARTIST_FIRST = "First artist"; public static final String ARTIST_STRING = "String"; diff --git a/src/integrationTest/java/org/opensearch/security/http/CommonProxyAuthenticationTests.java b/src/integrationTest/java/org/opensearch/security/http/CommonProxyAuthenticationTests.java new file mode 100644 index 0000000000..388a82ab7d --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/http/CommonProxyAuthenticationTests.java @@ -0,0 +1,255 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.security.http; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.List; + +import org.opensearch.test.framework.RolesMapping; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClientConfiguration; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; + +abstract class CommonProxyAuthenticationTests { + + protected static final String RESOURCE_AUTH_INFO = "/_opendistro/_security/authinfo"; + protected static final TestSecurityConfig.User USER_ADMIN = new TestSecurityConfig.User("admin").roles(ALL_ACCESS); + + protected static final String ATTRIBUTE_DEPARTMENT = "department"; + protected static final String ATTRIBUTE_SKILLS = "skills"; + + protected static final String USER_ATTRIBUTE_DEPARTMENT_NAME = "attr.proxy." + ATTRIBUTE_DEPARTMENT; + protected static final String USER_ATTRIBUTE_SKILLS_NAME = "attr.proxy." + ATTRIBUTE_SKILLS; + protected static final String USER_ATTRIBUTE_USERNAME_NAME = "attr.proxy.username"; + + protected static final String HEADER_PREFIX_CUSTOM_ATTRIBUTES = "x-custom-attr"; + protected static final String HEADER_PROXY_USER = "x-proxy-user"; + protected static final String HEADER_PROXY_ROLES = "x-proxy-roles"; + protected static final String HEADER_FORWARDED_FOR = "X-Forwarded-For"; + protected static final String HEADER_DEPARTMENT = HEADER_PREFIX_CUSTOM_ATTRIBUTES + ATTRIBUTE_DEPARTMENT; + protected static final String HEADER_SKILLS = HEADER_PREFIX_CUSTOM_ATTRIBUTES + ATTRIBUTE_SKILLS; + + protected static final String IP_PROXY = "127.0.0.10"; + protected static final String IP_NON_PROXY = "127.0.0.5"; + protected static final String IP_CLIENT = "127.0.0.1"; + + protected static final String USER_KIRK = "kirk"; + protected static final String USER_SPOCK = "spock"; + + protected static final String BACKEND_ROLE_FIRST_MATE = "firstMate"; + protected static final String BACKEND_ROLE_CAPTAIN = "captain"; + protected static final String DEPARTMENT_BRIDGE = "bridge"; + + protected static final String PERSONAL_INDEX_NAME_PATTERN = "personal-${" + USER_ATTRIBUTE_DEPARTMENT_NAME + "}-${" + USER_ATTRIBUTE_USERNAME_NAME + "}"; + protected static final String PERSONAL_INDEX_NAME_SPOCK = "personal-" + DEPARTMENT_BRIDGE + "-" + USER_SPOCK; + protected static final String PERSONAL_INDEX_NAME_KIRK = "personal-" + DEPARTMENT_BRIDGE + "-" + USER_KIRK; + + protected static final String POINTER_USERNAME = "/user_name"; + protected static final String POINTER_BACKEND_ROLES = "/backend_roles"; + protected static final String POINTER_ROLES = "/roles"; + protected static final String POINTER_CUSTOM_ATTRIBUTES = "/custom_attribute_names"; + protected static final String POINTER_TOTAL_HITS = "/hits/total/value"; + protected static final String POINTER_FIRST_DOCUMENT_ID = "/hits/hits/0/_id"; + protected static final String POINTER_FIRST_DOCUMENT_INDEX = "/hits/hits/0/_index"; + protected static final String POINTER_FIRST_DOCUMENT_SOURCE_TITLE = "/hits/hits/0/_source/title"; + + protected static final TestSecurityConfig.Role ROLE_ALL_INDEX_SEARCH = new TestSecurityConfig.Role("all-index-search").indexPermissions("indices:data/read/search") + .on("*"); + + protected static final TestSecurityConfig.Role ROLE_PERSONAL_INDEX_SEARCH = new TestSecurityConfig.Role("personal-index-search").indexPermissions("indices:data/read/search") + .on(PERSONAL_INDEX_NAME_PATTERN); + + protected static final RolesMapping ROLES_MAPPING_CAPTAIN = new RolesMapping(ROLE_PERSONAL_INDEX_SEARCH).backendRoles(BACKEND_ROLE_CAPTAIN); + + protected static final RolesMapping ROLES_MAPPING_FIRST_MATE = new RolesMapping(ROLE_ALL_INDEX_SEARCH) + .backendRoles(BACKEND_ROLE_FIRST_MATE); + + protected abstract LocalCluster getCluster(); + + protected void shouldAuthenticateWithBasicAuthWhenProxyAuthenticationIsConfigured() { + try(TestRestClient client = getCluster().getRestClient(USER_ADMIN)) { + TestRestClient.HttpResponse response = client.get(RESOURCE_AUTH_INFO); + + response.assertStatusCode(200); + } + } + + protected void shouldAuthenticateWithProxy_positiveUserKirk() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_KIRK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_KIRK)); + } + } + + protected void shouldAuthenticateWithProxy_positiveUserSpock() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_FIRST_MATE); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_SPOCK)); + } + } + + protected void shouldAuthenticateWithProxy_negativeWhenXffHeaderIsMissing() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_PROXY_USER, USER_KIRK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + } + } + + protected void shouldAuthenticateWithProxy_negativeWhenUserNameHeaderIsMissing() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + } + } + + protected void shouldAuthenticateWithProxyWhenRolesHeaderIsMissing() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_KIRK); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + String username = response.getTextFromJsonBody(POINTER_USERNAME); + assertThat(username, equalTo(USER_KIRK)); + } + } + + protected void shouldAuthenticateWithProxy_negativeWhenRequestWasNotSendByProxy() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_NON_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_KIRK); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(401); + } + } + + protected void shouldRetrieveEmptyListOfRoles() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(0)); + List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); + assertThat(roles, hasSize(0)); + } + } + + protected void shouldRetrieveSingleRoleFirstMate() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_FIRST_MATE); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(1)); + assertThat(backendRoles, contains(BACKEND_ROLE_FIRST_MATE)); + List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); + assertThat(roles, hasSize(1)); + assertThat(roles, contains(ROLE_ALL_INDEX_SEARCH.getName())); + } + } + + protected void shouldRetrieveSingleRoleCaptain() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(1)); + assertThat(backendRoles, contains(BACKEND_ROLE_CAPTAIN)); + List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); + assertThat(roles, hasSize(1)); + assertThat(roles, contains(ROLE_PERSONAL_INDEX_SEARCH.getName())); + } + } + + protected void shouldRetrieveMultipleRoles() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN + "," + BACKEND_ROLE_FIRST_MATE); + try(TestRestClient client = getCluster().createGenericClientRestClient(testRestClientConfiguration)) { + + TestRestClient.HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); + assertThat(backendRoles, hasSize(2)); + assertThat(backendRoles, containsInAnyOrder(BACKEND_ROLE_CAPTAIN, BACKEND_ROLE_FIRST_MATE)); + List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); + assertThat(roles, hasSize(2)); + assertThat(roles, containsInAnyOrder(ROLE_PERSONAL_INDEX_SEARCH.getName(), ROLE_ALL_INDEX_SEARCH.getName())); + } + } +} diff --git a/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java new file mode 100644 index 0000000000..0f95a8b1b8 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/http/ExtendedProxyAuthenticationTest.java @@ -0,0 +1,246 @@ +/* +* Copyright OpenSearch Contributors +* SPDX-License-Identifier: Apache-2.0 +* +* The OpenSearch Contributors require contributions made to +* this file be licensed under the Apache-2.0 license or a +* compatible open source license. +* +*/ +package org.opensearch.security.http; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.Map; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.opensearch.client.Client; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend; +import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator; +import org.opensearch.test.framework.XffConfig; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; +import org.opensearch.test.framework.cluster.TestRestClientConfiguration; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; +import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.opensearch.security.Song.SONGS; +import static org.opensearch.security.Song.TITLE_MAGNUM_OPUS; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class ExtendedProxyAuthenticationTest extends CommonProxyAuthenticationTests { + + + public static final String ID_ONE_1 = "one#1"; + public static final String ID_TWO_2 = "two#2"; + public static final Map PROXY_AUTHENTICATOR_CONFIG = Map.of( + "user_header", HEADER_PROXY_USER, + "roles_header", HEADER_PROXY_ROLES, + "attr_header_prefix", HEADER_PREFIX_CUSTOM_ATTRIBUTES + ); + + @ClassRule + public static final LocalCluster cluster = new LocalCluster.Builder() + .clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false) + .xff(new XffConfig(true).internalProxiesRegexp("127\\.0\\.0\\.10")) + .authc(new AuthcDomain("proxy_auth_domain", -5, true) + .httpAuthenticator(new HttpAuthenticator("extended-proxy").challenge(false).config(PROXY_AUTHENTICATOR_CONFIG)) + .backend(new AuthenticationBackend("noop"))) + .authc(AUTHC_HTTPBASIC_INTERNAL).users(USER_ADMIN).roles(ROLE_ALL_INDEX_SEARCH, ROLE_PERSONAL_INDEX_SEARCH) + .rolesMapping(ROLES_MAPPING_CAPTAIN, ROLES_MAPPING_FIRST_MATE).build(); + + @Override + protected LocalCluster getCluster() { + return cluster; + } + + @BeforeClass + public static void createTestData() { + try(Client client = cluster.getInternalNodeClient()){ + client.prepareIndex(PERSONAL_INDEX_NAME_SPOCK).setId(ID_ONE_1).setRefreshPolicy(IMMEDIATE).setSource(SONGS[0]).get(); + client.prepareIndex(PERSONAL_INDEX_NAME_KIRK).setId(ID_TWO_2).setRefreshPolicy(IMMEDIATE).setSource(SONGS[1]).get(); + } + } + + @Test + @Override + public void shouldAuthenticateWithBasicAuthWhenProxyAuthenticationIsConfigured() { + super.shouldAuthenticateWithBasicAuthWhenProxyAuthenticationIsConfigured(); + } + + @Test + @Override + public void shouldAuthenticateWithProxy_positiveUserKirk() throws IOException { + super.shouldAuthenticateWithProxy_positiveUserKirk(); + } + + @Test + @Override + public void shouldAuthenticateWithProxy_positiveUserSpock() throws IOException { + super.shouldAuthenticateWithProxy_positiveUserSpock(); + } + + @Test + @Override + public void shouldAuthenticateWithProxy_negativeWhenXffHeaderIsMissing() throws IOException { + super.shouldAuthenticateWithProxy_negativeWhenXffHeaderIsMissing(); + } + + @Test + @Override + public void shouldAuthenticateWithProxy_negativeWhenUserNameHeaderIsMissing() throws IOException { + super.shouldAuthenticateWithProxy_negativeWhenUserNameHeaderIsMissing(); + } + + @Test + @Override + public void shouldAuthenticateWithProxyWhenRolesHeaderIsMissing() throws IOException { + super.shouldAuthenticateWithProxyWhenRolesHeaderIsMissing(); + } + + @Test + @Override + public void shouldAuthenticateWithProxy_negativeWhenRequestWasNotSendByProxy() throws IOException { + super.shouldAuthenticateWithProxy_negativeWhenRequestWasNotSendByProxy(); + } + + @Test + @Override + public void shouldRetrieveEmptyListOfRoles() throws IOException { + super.shouldRetrieveEmptyListOfRoles(); + } + + @Test + @Override + public void shouldRetrieveSingleRoleFirstMate() throws IOException { + super.shouldRetrieveSingleRoleFirstMate(); + } + + @Test + @Override + public void shouldRetrieveSingleRoleCaptain() throws IOException { + super.shouldRetrieveSingleRoleCaptain(); + } + + @Test + @Override + public void shouldRetrieveMultipleRoles() throws IOException { + super.shouldRetrieveMultipleRoles(); + } + + // tests specific for extended proxy authentication + + @Test + public void shouldRetrieveCustomAttributeNameDepartment() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN) + .header(HEADER_DEPARTMENT, DEPARTMENT_BRIDGE); + try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List customAttributes = response.getTextArrayFromJsonBody(POINTER_CUSTOM_ATTRIBUTES); + assertThat(customAttributes, hasSize(2)); + assertThat(customAttributes, containsInAnyOrder(USER_ATTRIBUTE_USERNAME_NAME, USER_ATTRIBUTE_DEPARTMENT_NAME)); + } + } + + @Test + public void shouldRetrieveCustomAttributeNameSkills() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN) + .header(HEADER_SKILLS, "bilocation"); + try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List customAttributes = response.getTextArrayFromJsonBody(POINTER_CUSTOM_ATTRIBUTES); + assertThat(customAttributes, hasSize(2)); + assertThat(customAttributes, containsInAnyOrder(USER_ATTRIBUTE_USERNAME_NAME, USER_ATTRIBUTE_SKILLS_NAME)); + } + } + + @Test + public void shouldRetrieveMultipleCustomAttributes() throws IOException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN) + .header(HEADER_DEPARTMENT, DEPARTMENT_BRIDGE) + .header(HEADER_SKILLS, "bilocation"); + try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { + + HttpResponse response = client.getAuthInfo(); + + response.assertStatusCode(200); + List customAttributes = response.getTextArrayFromJsonBody(POINTER_CUSTOM_ATTRIBUTES); + assertThat(customAttributes, hasSize(3)); + assertThat(customAttributes, containsInAnyOrder( + USER_ATTRIBUTE_DEPARTMENT_NAME, + USER_ATTRIBUTE_USERNAME_NAME, + USER_ATTRIBUTE_SKILLS_NAME) + ); + } + } + + @Test + public void shouldRetrieveUserRolesAndAttributesSoThatAccessToPersonalIndexIsPossible_positive() throws UnknownHostException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN) + .header(HEADER_DEPARTMENT, DEPARTMENT_BRIDGE); + try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { + + HttpResponse response = client.get("/" + PERSONAL_INDEX_NAME_SPOCK + "/_search"); + + response.assertStatusCode(200); + assertThat(response.getLongFromJsonBody(POINTER_TOTAL_HITS), equalTo(1L)); + assertThat(response.getTextFromJsonBody(POINTER_FIRST_DOCUMENT_ID), equalTo(ID_ONE_1)); + assertThat(response.getTextFromJsonBody(POINTER_FIRST_DOCUMENT_INDEX), equalTo(PERSONAL_INDEX_NAME_SPOCK)); + assertThat(response.getTextFromJsonBody(POINTER_FIRST_DOCUMENT_SOURCE_TITLE), equalTo(TITLE_MAGNUM_OPUS)); + } + } + + @Test + public void shouldRetrieveUserRolesAndAttributesSoThatAccessToPersonalIndexIsPossible_negative() throws UnknownHostException { + TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() + .sourceInetAddress(InetAddress.getByName(IP_PROXY)) + .header(HEADER_FORWARDED_FOR, IP_CLIENT) + .header(HEADER_PROXY_USER, USER_SPOCK) + .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN) + .header(HEADER_DEPARTMENT, DEPARTMENT_BRIDGE); + try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { + + HttpResponse response = client.get("/" + PERSONAL_INDEX_NAME_KIRK + "/_search"); + + response.assertStatusCode(403); + } + } + +} diff --git a/src/integrationTest/java/org/opensearch/security/http/ProxyAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/ProxyAuthenticationTest.java index 3b4ca22399..ffa3126b88 100644 --- a/src/integrationTest/java/org/opensearch/security/http/ProxyAuthenticationTest.java +++ b/src/integrationTest/java/org/opensearch/security/http/ProxyAuthenticationTest.java @@ -10,8 +10,6 @@ package org.opensearch.security.http; import java.io.IOException; -import java.net.InetAddress; -import java.util.List; import java.util.Map; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; @@ -19,68 +17,24 @@ import org.junit.Test; import org.junit.runner.RunWith; -import org.opensearch.test.framework.RolesMapping; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AuthenticationBackend; import org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.HttpAuthenticator; -import org.opensearch.test.framework.TestSecurityConfig.Role; -import org.opensearch.test.framework.TestSecurityConfig.User; import org.opensearch.test.framework.XffConfig; import org.opensearch.test.framework.cluster.ClusterManager; import org.opensearch.test.framework.cluster.LocalCluster; -import org.opensearch.test.framework.cluster.TestRestClient; -import org.opensearch.test.framework.cluster.TestRestClient.HttpResponse; -import org.opensearch.test.framework.cluster.TestRestClientConfiguration; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -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 org.opensearch.test.framework.TestSecurityConfig.Role.ALL_ACCESS; @RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) @ThreadLeakScope(ThreadLeakScope.Scope.NONE) -public class ProxyAuthenticationTest { +public class ProxyAuthenticationTest extends CommonProxyAuthenticationTests { - public static final String RESOURCE_AUTH_INFO = "/_opendistro/_security/authinfo"; - public static final User USER_ADMIN = new User("admin").roles(ALL_ACCESS); - - public static final String HEADER_PROXY_USER = "x-proxy-user"; - public static final String HEADER_PROXY_ROLES = "x-proxy-roles"; - public static final String HEADER_FORWARDED_FOR = "X-Forwarded-For"; - - public static final String IP_PROXY = "127.0.0.10"; - public static final String IP_NON_PROXY = "127.0.0.5"; - public static final String IP_CLIENT = "127.0.0.1"; - - public static final String USER_KIRK = "kirk"; - public static final String USER_SPOCK = "spock"; - - public static final String BACKEND_ROLE_FIRST_MATE = "firstMate"; - public static final String BACKEND_ROLE_CAPTAIN = "captain"; - - public static final String POINTER_USERNAME = "/user_name"; - public static final String POINTER_BACKEND_ROLES = "/backend_roles"; - public static final String POINTER_ROLES = "/roles"; - - public static final Map PROXY_AUTHENTICATOR_CONFIG = Map.of( + private static final Map PROXY_AUTHENTICATOR_CONFIG = Map.of( "user_header", HEADER_PROXY_USER, "roles_header", HEADER_PROXY_ROLES ); - public static final Role ROLE_ALL_INDEX_SEARCH = new Role("all-index-search").indexPermissions("indices:data/read/search") - .on("*"); - - public static final Role ROLE_ALL_INDEX_GET = new Role("all-index-get").indexPermissions("indices:data/read/get") - .on("*"); - - public static final RolesMapping ROLES_MAPPING_CAPTAIN = new RolesMapping(ROLE_ALL_INDEX_GET).backendRoles(BACKEND_ROLE_CAPTAIN); - - public static final RolesMapping ROLES_MAPPING_FIRST_MATE = new RolesMapping(ROLE_ALL_INDEX_SEARCH) - .backendRoles(BACKEND_ROLE_FIRST_MATE); - @ClassRule public static final LocalCluster cluster = new LocalCluster.Builder() .clusterManager(ClusterManager.SINGLENODE).anonymousAuth(false) @@ -88,188 +42,77 @@ public class ProxyAuthenticationTest { .authc(new AuthcDomain("proxy_auth_domain", -5, true) .httpAuthenticator(new HttpAuthenticator("proxy").challenge(false).config(PROXY_AUTHENTICATOR_CONFIG)) .backend(new AuthenticationBackend("noop"))) - .authc(AUTHC_HTTPBASIC_INTERNAL).users(USER_ADMIN).roles(ROLE_ALL_INDEX_SEARCH, ROLE_ALL_INDEX_GET) + .authc(AUTHC_HTTPBASIC_INTERNAL).users(USER_ADMIN).roles(ROLE_ALL_INDEX_SEARCH, ROLE_PERSONAL_INDEX_SEARCH) .rolesMapping(ROLES_MAPPING_CAPTAIN, ROLES_MAPPING_FIRST_MATE).build(); + @Override + protected LocalCluster getCluster() { + return cluster; + } + @Test + @Override public void shouldAuthenticateWithBasicAuthWhenProxyAuthenticationIsConfigured() { - try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) { - HttpResponse response = client.get(RESOURCE_AUTH_INFO); - - response.assertStatusCode(200); - } + super.shouldAuthenticateWithBasicAuthWhenProxyAuthenticationIsConfigured(); } @Test + @Override public void shouldAuthenticateWithProxy_positiveUserKirk() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_KIRK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_KIRK)); - } + super.shouldAuthenticateWithProxy_positiveUserKirk(); } @Test + @Override public void shouldAuthenticateWithProxy_positiveUserSpock() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_SPOCK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_FIRST_MATE); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_SPOCK)); - } + super.shouldAuthenticateWithProxy_positiveUserSpock(); } @Test + @Override public void shouldAuthenticateWithProxy_negativeWhenXffHeaderIsMissing() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_PROXY_USER, USER_KIRK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - } + super.shouldAuthenticateWithProxy_negativeWhenXffHeaderIsMissing(); } @Test + @Override public void shouldAuthenticateWithProxy_negativeWhenUserNameHeaderIsMissing() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - } + super.shouldAuthenticateWithProxy_negativeWhenUserNameHeaderIsMissing(); } @Test + @Override public void shouldAuthenticateWithProxyWhenRolesHeaderIsMissing() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_KIRK); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - String username = response.getTextFromJsonBody(POINTER_USERNAME); - assertThat(username, equalTo(USER_KIRK)); - } + super.shouldAuthenticateWithProxyWhenRolesHeaderIsMissing(); } @Test + @Override public void shouldAuthenticateWithProxy_negativeWhenRequestWasNotSendByProxy() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_NON_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_KIRK); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(401); - } + super.shouldAuthenticateWithProxy_negativeWhenRequestWasNotSendByProxy(); } @Test + @Override public void shouldRetrieveEmptyListOfRoles() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_SPOCK); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(0)); - List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); - assertThat(roles, hasSize(0)); - } + super.shouldRetrieveEmptyListOfRoles(); } @Test + @Override public void shouldRetrieveSingleRoleFirstMate() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_SPOCK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_FIRST_MATE); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(1)); - assertThat(backendRoles, contains(BACKEND_ROLE_FIRST_MATE)); - List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); - assertThat(roles, hasSize(1)); - assertThat(roles, contains(ROLE_ALL_INDEX_SEARCH.getName())); - } + super.shouldRetrieveSingleRoleFirstMate(); } @Test + @Override public void shouldRetrieveSingleRoleCaptain() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_SPOCK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(1)); - assertThat(backendRoles, contains(BACKEND_ROLE_CAPTAIN)); - List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); - assertThat(roles, hasSize(1)); - assertThat(roles, contains(ROLE_ALL_INDEX_GET.getName())); - } + super.shouldRetrieveSingleRoleCaptain(); } @Test + @Override public void shouldRetrieveMultipleRoles() throws IOException { - TestRestClientConfiguration testRestClientConfiguration = new TestRestClientConfiguration() - .sourceInetAddress(InetAddress.getByName(IP_PROXY)) - .header(HEADER_FORWARDED_FOR, IP_CLIENT) - .header(HEADER_PROXY_USER, USER_SPOCK) - .header(HEADER_PROXY_ROLES, BACKEND_ROLE_CAPTAIN + "," + BACKEND_ROLE_FIRST_MATE); - try(TestRestClient client = cluster.createGenericClientRestClient(testRestClientConfiguration)) { - - HttpResponse response = client.getAuthInfo(); - - response.assertStatusCode(200); - List backendRoles = response.getTextArrayFromJsonBody(POINTER_BACKEND_ROLES); - assertThat(backendRoles, hasSize(2)); - assertThat(backendRoles, containsInAnyOrder(BACKEND_ROLE_CAPTAIN, BACKEND_ROLE_FIRST_MATE)); - List roles = response.getTextArrayFromJsonBody(POINTER_ROLES); - assertThat(roles, hasSize(2)); - assertThat(roles, containsInAnyOrder(ROLE_ALL_INDEX_GET.getName(), ROLE_ALL_INDEX_SEARCH.getName())); - } + super.shouldRetrieveMultipleRoles(); } } diff --git a/src/integrationTest/java/org/opensearch/test/framework/cluster/OpenSearchClientProvider.java b/src/integrationTest/java/org/opensearch/test/framework/cluster/OpenSearchClientProvider.java index ab8a27a8a2..98359c26da 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/cluster/OpenSearchClientProvider.java +++ b/src/integrationTest/java/org/opensearch/test/framework/cluster/OpenSearchClientProvider.java @@ -123,16 +123,16 @@ public TlsDetails create(final SSLEngine sslEngine) { }) .build(); - final AsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create() - .setTlsStrategy(tlsStrategy) - .build(); - - if(credentialsProvider != null) { - httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); - } - httpClientBuilder.setDefaultHeaders(defaultHeaders); - httpClientBuilder.setConnectionManager(cm); - return httpClientBuilder; + final AsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create() + .setTlsStrategy(tlsStrategy) + .build(); + + if(credentialsProvider != null) { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + httpClientBuilder.setDefaultHeaders(defaultHeaders); + httpClientBuilder.setConnectionManager(cm); + return httpClientBuilder; }; InetSocketAddress httpAddress = getHttpAddress();