From 7e1b7867dd73a0ad6f0bbfd21c9880cc8c665127 Mon Sep 17 00:00:00 2001 From: Ryan Liang <109499885+RyanL1997@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:58:53 -0700 Subject: [PATCH] [Feature/Extension] Add integration test case for OBO hostmapping (#3270) ### Description Add integration test case for OBO hostmapping * Category (Enhancement, New feature, Bug fix, Test fix, Refactoring, Maintenance, Documentation) Test Enhancement ### Issues Resolved * Resolve https://github.com/opensearch-project/security/issues/3222 ### Check List - [x] New functionality includes testing - [ ] New functionality has been documented - [x] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). --------- Signed-off-by: Ryan Liang --- .../http/OnBehalfOfJwtAuthenticationTest.java | 43 +++++++++++++++++-- .../test/framework/RolesMapping.java | 13 ++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/integrationTest/java/org/opensearch/security/http/OnBehalfOfJwtAuthenticationTest.java b/src/integrationTest/java/org/opensearch/security/http/OnBehalfOfJwtAuthenticationTest.java index 4fc5b059ea..6e4fc1c89d 100644 --- a/src/integrationTest/java/org/opensearch/security/http/OnBehalfOfJwtAuthenticationTest.java +++ b/src/integrationTest/java/org/opensearch/security/http/OnBehalfOfJwtAuthenticationTest.java @@ -12,11 +12,15 @@ package org.opensearch.security.http; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.message.BasicHeader; @@ -25,7 +29,9 @@ import org.junit.Test; import org.junit.runner.RunWith; +import org.opensearch.security.authtoken.jwt.EncryptionDecryptionUtil; import org.opensearch.test.framework.OnBehalfOfConfig; +import org.opensearch.test.framework.RolesMapping; import org.opensearch.test.framework.TestSecurityConfig; import org.opensearch.test.framework.cluster.ClusterManager; import org.opensearch.test.framework.cluster.LocalCluster; @@ -65,28 +71,42 @@ public class OnBehalfOfJwtAuthenticationTest { public static final String OBO_TOKEN_REASON = "{\"reason\":\"Test generation\"}"; public static final String OBO_ENDPOINT_PREFIX = "_plugins/_security/api/generateonbehalfoftoken"; public static final String OBO_DESCRIPTION = "{\"description\":\"Testing\", \"service\":\"self-issued\"}"; + public static final String HOST_MAPPING_IP = "127.0.0.1"; + public static final String OBO_USER_NAME_WITH_HOST_MAPPING = "obo_user_with_ip_role_mapping"; public static final String CURRENT_AND_NEW_PASSWORDS = "{ \"current_password\": \"" + DEFAULT_PASSWORD + "\", \"password\": \"" + NEW_PASSWORD + "\" }"; + private static final TestSecurityConfig.Role ROLE_WITH_OBO_PERM = new TestSecurityConfig.Role("obo_access_role").clusterPermissions( + "security:obo/create" + ); + + private static final TestSecurityConfig.Role ROLE_WITH_NO_OBO_PERM = new TestSecurityConfig.Role("obo_user_no_perm"); + protected final static TestSecurityConfig.User OBO_USER = new TestSecurityConfig.User(OBO_USER_NAME_WITH_PERM).roles( - new TestSecurityConfig.Role("obo_access_role").clusterPermissions("security:obo/create") + ROLE_WITH_OBO_PERM ); protected final static TestSecurityConfig.User OBO_USER_NO_PERM = new TestSecurityConfig.User(OBO_USER_NAME_NO_PERM).roles( - new TestSecurityConfig.Role("obo_user_no_perm") + ROLE_WITH_NO_OBO_PERM ); + private static final TestSecurityConfig.Role HOST_MAPPING_ROLE = new TestSecurityConfig.Role("host_mapping_role"); + + protected final static TestSecurityConfig.User HOST_MAPPING_OBO_USER = new TestSecurityConfig.User(OBO_USER_NAME_WITH_HOST_MAPPING) + .roles(HOST_MAPPING_ROLE, ROLE_WITH_OBO_PERM); + @ClassRule public static final LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.SINGLENODE) .anonymousAuth(false) - .users(ADMIN_USER, OBO_USER, OBO_USER_NO_PERM) + .users(ADMIN_USER, OBO_USER, OBO_USER_NO_PERM, HOST_MAPPING_OBO_USER) .nodeSettings( Map.of(SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true, SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_admin__all_access")) ) .authc(AUTHC_HTTPBASIC_INTERNAL) + .rolesMapping(new RolesMapping(HOST_MAPPING_ROLE).hostIPs(HOST_MAPPING_IP)) .onBehalfOf(new OnBehalfOfConfig().oboEnabled(oboEnabled).signingKey(signingKey).encryptionKey(encryptionKey)) .build(); @@ -141,6 +161,23 @@ public void shouldNotAuthenticateForNonAdminUserWithoutOBOPermission() { } } + @Test + public void shouldNotIncludeRolesFromHostMappingInOBOToken() { + String oboToken = generateOboToken(OBO_USER_NAME_WITH_HOST_MAPPING, DEFAULT_PASSWORD); + + Claims claims = Jwts.parserBuilder().setSigningKey(signingKey).build().parseClaimsJws(oboToken).getBody(); + + Object er = claims.get("er"); + EncryptionDecryptionUtil encryptionDecryptionUtil = new EncryptionDecryptionUtil(encryptionKey); + String rolesClaim = encryptionDecryptionUtil.decrypt(er.toString()); + List roles = Arrays.stream(rolesClaim.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toUnmodifiableList()); + + Assert.assertFalse(roles.contains("host_mapping_role")); + } + private String generateOboToken(String username, String password) { try (TestRestClient client = cluster.getRestClient(username, password)) { client.assertCorrectCredentials(username); diff --git a/src/integrationTest/java/org/opensearch/test/framework/RolesMapping.java b/src/integrationTest/java/org/opensearch/test/framework/RolesMapping.java index 75c0325474..997e7e128b 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/RolesMapping.java +++ b/src/integrationTest/java/org/opensearch/test/framework/RolesMapping.java @@ -36,6 +36,7 @@ public class RolesMapping implements ToXContentObject { * Backend role names */ private List backendRoles; + private List hostIPs; private boolean reserved = false; @@ -47,6 +48,7 @@ public RolesMapping(Role role) { requireNonNull(role); this.roleName = requireNonNull(role.getName()); this.backendRoles = new ArrayList<>(); + this.hostIPs = new ArrayList<>(); } /** @@ -59,6 +61,16 @@ public RolesMapping backendRoles(String... backendRoles) { return this; } + /** + * Defines host IP address + * @param hostIPs host IP address + * @return current {@link RolesMapping} instance + */ + public RolesMapping hostIPs(String... hostIPs) { + this.hostIPs.addAll(Arrays.asList(hostIPs)); + return this; + } + /** * Determines if role is reserved * @param reserved true for reserved roles @@ -89,6 +101,7 @@ public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params xContentBuilder.startObject(); xContentBuilder.field("reserved", reserved); xContentBuilder.field("backend_roles", backendRoles); + xContentBuilder.field("hosts", hostIPs); xContentBuilder.endObject(); return xContentBuilder; }