Skip to content

Commit

Permalink
Basic test framework
Browse files Browse the repository at this point in the history
Signed-off-by: Stephen Crawford <[email protected]>
  • Loading branch information
stephen-crawford committed Sep 14, 2023
1 parent 4bbbc37 commit 2110225
Show file tree
Hide file tree
Showing 140 changed files with 11,773 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,11 @@
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 @@ -29,9 +25,7 @@
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 All @@ -57,11 +51,11 @@ public class OnBehalfOfJwtAuthenticationTest {

private static Boolean oboEnabled = true;
private static final String signingKey = Base64.getEncoder()
.encodeToString(
"jwt signing key for an on behalf of token authentication backend for testing of OBO authentication".getBytes(
StandardCharsets.UTF_8
)
);
.encodeToString(
"jwt signing key for an on behalf of token authentication backend for testing of OBO authentication".getBytes(
StandardCharsets.UTF_8
)
);
private static final String encryptionKey = Base64.getEncoder().encodeToString("encryptionKey".getBytes(StandardCharsets.UTF_8));
public static final String ADMIN_USER_NAME = "admin";
public static final String OBO_USER_NAME_WITH_PERM = "obo_user";
Expand All @@ -71,44 +65,30 @@ 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");
+ DEFAULT_PASSWORD
+ "\", \"password\": \""
+ NEW_PASSWORD
+ "\" }";

protected final static TestSecurityConfig.User OBO_USER = new TestSecurityConfig.User(OBO_USER_NAME_WITH_PERM).roles(
ROLE_WITH_OBO_PERM
new TestSecurityConfig.Role("obo_access_role").clusterPermissions("security:obo/create")
);

protected final static TestSecurityConfig.User OBO_USER_NO_PERM = new TestSecurityConfig.User(OBO_USER_NAME_NO_PERM).roles(
ROLE_WITH_NO_OBO_PERM
new TestSecurityConfig.Role("obo_user_no_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, 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();
.anonymousAuth(false)
.users(ADMIN_USER, OBO_USER, OBO_USER_NO_PERM)
.nodeSettings(
Map.of(SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true, SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_admin__all_access"))
)
.authc(AUTHC_HTTPBASIC_INTERNAL)
.onBehalfOf(new OnBehalfOfConfig().oboEnabled(oboEnabled).signingKey(signingKey).encryptionKey(encryptionKey))
.build();

@Test
public void shouldAuthenticateWithOBOTokenEndPoint() {
Expand Down Expand Up @@ -161,32 +141,15 @@ 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);
TestRestClient.HttpResponse response = client.postJson(OBO_ENDPOINT_PREFIX, OBO_TOKEN_REASON);
response.assertStatusCode(200);
Map<String, Object> oboEndPointResponse = response.getBodyAs(Map.class);
assertThat(
oboEndPointResponse,
allOf(aMapWithSize(3), hasKey("user"), hasKey("authenticationToken"), hasKey("durationSeconds"))
oboEndPointResponse,
allOf(aMapWithSize(3), hasKey("user"), hasKey("authenticationToken"), hasKey("durationSeconds"))
);
return oboEndPointResponse.get("authenticationToken").toString();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* 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.test.framework;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;

public class AuditCompliance implements ToXContentObject {

private boolean enabled = false;

private Boolean writeLogDiffs;

private List<String> readIgnoreUsers;

private List<String> writeWatchedIndices;

private List<String> writeIgnoreUsers;

private Boolean readMetadataOnly;

private Boolean writeMetadataOnly;

private Boolean externalConfig;

private Boolean internalConfig;

public AuditCompliance enabled(boolean enabled) {
this.enabled = enabled;
this.writeLogDiffs = false;
this.readIgnoreUsers = Collections.emptyList();
this.writeWatchedIndices = Collections.emptyList();
this.writeIgnoreUsers = Collections.emptyList();
this.readMetadataOnly = false;
this.writeMetadataOnly = false;
this.externalConfig = false;
this.internalConfig = false;
return this;
}

public AuditCompliance writeLogDiffs(boolean writeLogDiffs) {
this.writeLogDiffs = writeLogDiffs;
return this;
}

public AuditCompliance readIgnoreUsers(List<String> list) {
this.readIgnoreUsers = list;
return this;
}

public AuditCompliance writeWatchedIndices(List<String> list) {
this.writeWatchedIndices = list;
return this;
}

public AuditCompliance writeIgnoreUsers(List<String> list) {
this.writeIgnoreUsers = list;
return this;
}

public AuditCompliance readMetadataOnly(boolean readMetadataOnly) {
this.readMetadataOnly = readMetadataOnly;
return this;
}

public AuditCompliance writeMetadataOnly(boolean writeMetadataOnly) {
this.writeMetadataOnly = writeMetadataOnly;
return this;
}

public AuditCompliance externalConfig(boolean externalConfig) {
this.externalConfig = externalConfig;
return this;
}

public AuditCompliance internalConfig(boolean internalConfig) {
this.internalConfig = internalConfig;
return this;
}

@Override
public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException {
xContentBuilder.startObject();
xContentBuilder.field("enabled", enabled);
xContentBuilder.field("write_log_diffs", writeLogDiffs);
xContentBuilder.field("read_ignore_users", readIgnoreUsers);
xContentBuilder.field("write_watched_indices", writeWatchedIndices);
xContentBuilder.field("write_ignore_users", writeIgnoreUsers);
xContentBuilder.field("read_metadata_only", readMetadataOnly);
xContentBuilder.field("write_metadata_only", writeMetadataOnly);
xContentBuilder.field("external_config", externalConfig);
xContentBuilder.field("internal_config", internalConfig);
xContentBuilder.endObject();
return xContentBuilder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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.test.framework;

import java.io.IOException;

import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;

public class AuditConfiguration implements ToXContentObject {
private final boolean enabled;

private AuditFilters filters;

private AuditCompliance compliance;

public AuditConfiguration(boolean enabled) {
this.filters = new AuditFilters();
this.compliance = new AuditCompliance();
this.enabled = enabled;
}

public boolean isEnabled() {
return enabled;
}

public AuditConfiguration filters(AuditFilters filters) {
this.filters = filters;
return this;
}

public AuditConfiguration compliance(AuditCompliance auditCompliance) {
this.compliance = auditCompliance;
return this;
}

@Override
public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException {
// json built here must be deserialized to org.opensearch.security.auditlog.config.AuditConfig
xContentBuilder.startObject();
xContentBuilder.field("enabled", enabled);

xContentBuilder.field("audit", filters);
xContentBuilder.field("compliance", compliance);

xContentBuilder.endObject();
return xContentBuilder;
}
}
Loading

0 comments on commit 2110225

Please sign in to comment.