Skip to content

Commit

Permalink
[Feature/Extension] Add integration test case for OBO hostmapping (#3270
Browse files Browse the repository at this point in the history
)

### Description
Add integration test case for OBO hostmapping

* Category (Enhancement, New feature, Bug fix, Test fix, Refactoring,
Maintenance, Documentation)
Test Enhancement

### Issues Resolved
* Resolve #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 <jiallian@amazon.com>
  • Loading branch information
RyanL1997 authored Sep 7, 2023
1 parent 00167b3 commit 7e1b786
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class RolesMapping implements ToXContentObject {
* Backend role names
*/
private List<String> backendRoles;
private List<String> hostIPs;

private boolean reserved = false;

Expand All @@ -47,6 +48,7 @@ public RolesMapping(Role role) {
requireNonNull(role);
this.roleName = requireNonNull(role.getName());
this.backendRoles = new ArrayList<>();
this.hostIPs = new ArrayList<>();
}

/**
Expand All @@ -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 <code>true</code> for reserved roles
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 7e1b786

Please sign in to comment.