Skip to content

Commit

Permalink
Test related to security plugin configuration updates.
Browse files Browse the repository at this point in the history
Signed-off-by: Lukasz Soszynski <[email protected]>
  • Loading branch information
lukasz-soszynski-eliatra committed Oct 13, 2022
1 parent 16b1676 commit 079d7f0
Show file tree
Hide file tree
Showing 21 changed files with 669 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;

class ConfigurationFiles {

public static void createRoleMappingFile(File destination) {
String resource = "roles_mapping.yml";
copyResourceToFile(resource, destination);
}

public static Path createConfigurationDirectory() {
try {
Path tempDirectory = Files.createTempDirectory("test-security-config");
String[] configurationFiles = {
"config.yml",
"action_groups.yml",
"config.yml",
"internal_users.yml",
"roles.yml",
"roles_mapping.yml",
"security_tenants.yml",
"tenants.yml"
};
for (String fileName : configurationFiles) {
Path configFileDestination = tempDirectory.resolve(fileName);
copyResourceToFile(fileName, configFileDestination.toFile());
}
return tempDirectory.toAbsolutePath();
} catch (IOException ex) {
throw new RuntimeException("Cannot create directory with security plugin configuration.", ex);
}
}

private static void copyResourceToFile(String resource, File destination) {
try(InputStream input = ConfigurationFiles.class.getClassLoader().getResourceAsStream(resource)) {
Objects.requireNonNull(input, "Cannot find source resource " + resource);
try(OutputStream output = new FileOutputStream(destination)) {
input.transferTo(output);
}
} catch (IOException e) {
throw new RuntimeException("Cannot create file with security plugin configuration", e);
}
}
}
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.security;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import org.apache.commons.io.FileUtils;
import org.awaitility.Awaitility;
import org.junit.AfterClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.opensearch.test.framework.cluster.ClusterManager;
import org.opensearch.test.framework.cluster.LocalCluster;
import org.opensearch.test.framework.cluster.TestRestClient;

import static org.hamcrest.Matchers.equalTo;

@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class)
@ThreadLeakScope(ThreadLeakScope.Scope.NONE)
public class DefaultConfigurationTests {

private final static Path configurationFolder = ConfigurationFiles.createConfigurationDirectory();

@ClassRule
public static LocalCluster cluster = new LocalCluster.Builder()
.clusterManager(ClusterManager.SINGLENODE)
.nodeSettings(Map.of("plugins.security.allow_default_init_securityindex", true))
.defaultConfigurationInitDirectory(configurationFolder.toString())
.loadConfigurationIntoIndex(false)
.build();

@AfterClass
public static void cleanConfigurationDirectory() throws IOException {
FileUtils.deleteDirectory(configurationFolder.toFile());
}

@Test
public void shouldLoadDefaultConfiguration() {
try(TestRestClient client = cluster.getRestClient("new-user", "secret")) {
Awaitility.await().alias("Load default configuration")
.until(() -> client.getAuthInfo().getStatusCode(), equalTo(200));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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;

import java.io.File;

import org.opensearch.security.tools.SecurityAdmin;
import org.opensearch.test.framework.certificate.TestCertificates;

import static java.util.Objects.requireNonNull;

class SecurityAdminLauncher {

private final TestCertificates certificates;
private int port;

public SecurityAdminLauncher(int port, TestCertificates certificates) {
this.port = port;
this.certificates = requireNonNull(certificates, "Certificates are required to communicate with cluster.");
}

public int updateRoleMappings(File roleMappingsConfigurationFile) throws Exception {
String[] commandLineArguments = {"-cacert", certificates.getRootCertificate().getAbsolutePath(),
"-cert", certificates.getAdminCertificate().getAbsolutePath(),
"-key", certificates.getAdminKey(null).getAbsolutePath(),
"-nhnv",
"-p", String.valueOf(port),
"-f", roleMappingsConfigurationFile.getAbsolutePath(),
"-t", "rolesmapping"
};

return SecurityAdmin.execute(commandLineArguments);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* 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;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
import org.awaitility.Awaitility;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;

import org.opensearch.client.Client;
import org.opensearch.test.framework.TestSecurityConfig.Role;
import org.opensearch.test.framework.TestSecurityConfig.User;
import org.opensearch.test.framework.certificate.TestCertificates;
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 static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
import static org.opensearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.opensearch.security.support.ConfigConstants.SECURITY_BACKGROUND_INIT_IF_SECURITYINDEX_NOT_EXIST;
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ROLES_ENABLED;
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 SecurityConfigurationTests {

private static final User USER_ADMIN = new User("admin").roles(ALL_ACCESS);
private static final User LIMITED_USER = new User("limited-user")
.roles(new Role("limited-role").indexPermissions("indices:data/read/search", "indices:data/read/get").on("user-${user.name}"));
public static final String LIMITED_USER_INDEX = "user-" + LIMITED_USER.getName();
public static final String ADDITIONAL_USER_1 = "additional00001";
public static final String ADDITIONAL_PASSWORD_1 = ADDITIONAL_USER_1;

public static final String ADDITIONAL_USER_2 = "additional2";
public static final String ADDITIONAL_PASSWORD_2 = ADDITIONAL_USER_2;
public static final String CREATE_USER_BODY = "{\"password\": \"%s\",\"opendistro_security_roles\": []}";
public static final String INTERNAL_USERS_RESOURCE = "/_plugins/_security/api/internalusers/";
public static final String ID_1 = "one";
public static final String PROHIBITED_INDEX = "prohibited";
public static final String ID_2 = "two";

@ClassRule
public static LocalCluster cluster = new LocalCluster.Builder()
.clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS)
.authc(AUTHC_HTTPBASIC_INTERNAL).users(USER_ADMIN, LIMITED_USER).anonymousAuth(false)
.nodeSettings(Map.of(SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_" + USER_ADMIN.getName() +"__" + ALL_ACCESS.getName()),
SECURITY_BACKGROUND_INIT_IF_SECURITYINDEX_NOT_EXIST, true))
.build();

@Rule
public TemporaryFolder configurationDirectory = new TemporaryFolder();

@BeforeClass
public static void initData() {
try(Client client = cluster.getInternalNodeClient()){
client.prepareIndex(LIMITED_USER_INDEX).setId(ID_1).setRefreshPolicy(IMMEDIATE).setSource("foo", "bar").get();
client.prepareIndex(PROHIBITED_INDEX).setId(ID_2).setRefreshPolicy(IMMEDIATE).setSource("three", "four").get();
}
}

@Test
public void shouldCreateUserViaRestApi_success() {
try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
HttpResponse httpResponse = client.putJson(INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_1, String.format(CREATE_USER_BODY,
ADDITIONAL_PASSWORD_1));

assertThat(httpResponse, notNullValue());
assertThat(httpResponse.getStatusCode(), equalTo(201));
}
try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
client.assertCorrectCredentials();
}
try(TestRestClient client = cluster.getRestClient(ADDITIONAL_USER_1, ADDITIONAL_PASSWORD_1)) {
client.assertCorrectCredentials();
}
}

@Test
public void shouldCreateUserViaRestApi_failure() {
try(TestRestClient client = cluster.getRestClient(LIMITED_USER)) {
HttpResponse httpResponse = client.putJson(INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_1, String.format(CREATE_USER_BODY,
ADDITIONAL_PASSWORD_1));

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(403);
}
}

@Test
public void shouldAuthenticateAsAdminWithCertificate_positive() {
try(TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) {
HttpResponse httpResponse = client.get("/_plugins/_security/whoami");

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(200);
assertThat(httpResponse.getTextFromJsonBody("/is_admin"), equalTo("true"));
}
}

@Test
public void shouldAuthenticateAsAdminWithCertificate_negativeSelfSignedCertificate() {
TestCertificates testCertificates = cluster.getTestCertificates();
try(TestRestClient client = cluster.getRestClient(testCertificates.createSelfSignedCertificate("CN=bond"))) {
HttpResponse httpResponse = client.get("/_plugins/_security/whoami");

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(200);
assertThat(httpResponse.getTextFromJsonBody("/is_admin"), equalTo("false"));
}
}

@Test
public void shouldAuthenticateAsAdminWithCertificate_negativeIncorrectDn() {
TestCertificates testCertificates = cluster.getTestCertificates();
try(TestRestClient client = cluster.getRestClient(testCertificates.createAdminCertificate("CN=non_admin"))) {
HttpResponse httpResponse = client.get("/_plugins/_security/whoami");

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(200);
assertThat(httpResponse.getTextFromJsonBody("/is_admin"), equalTo("false"));
}
}

@Test
public void shouldCreateUserViaRestApiWhenAdminIsAuthenticatedViaCertificate_positive() {
try(TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) {

HttpResponse httpResponse = client.putJson(INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_2, String.format(CREATE_USER_BODY,
ADDITIONAL_PASSWORD_2));

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(201);
}
try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
client.assertCorrectCredentials();
}
try(TestRestClient client = cluster.getRestClient(ADDITIONAL_USER_2, ADDITIONAL_PASSWORD_2)) {
client.assertCorrectCredentials();
}
}

@Test
public void shouldCreateUserViaRestApiWhenAdminIsAuthenticatedViaCertificate_negative() {
TestCertificates testCertificates = cluster.getTestCertificates();
try(TestRestClient client = cluster.getRestClient(testCertificates.createSelfSignedCertificate("CN=attacker"))) {
HttpResponse httpResponse = client.putJson(INTERNAL_USERS_RESOURCE + ADDITIONAL_USER_2, String.format(CREATE_USER_BODY,
ADDITIONAL_PASSWORD_2));

assertThat(httpResponse, notNullValue());
httpResponse.assertStatusCode(401);
}
}

@Test
public void shouldStillWorkAfterUpdateOfSecurityConfig() {
List<User> users = new ArrayList<>(cluster.getConfiguredUsers());
User newUser = new User("new-user");
users.add(newUser);

cluster.updateUserConfiguration(users);

try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
client.assertCorrectCredentials();
}
try(TestRestClient client = cluster.getRestClient(newUser)) {
client.assertCorrectCredentials();
}
}

@Test
public void shouldAccessIndexWithPlaceholder_positive() {
try(TestRestClient client = cluster.getRestClient(LIMITED_USER)) {
HttpResponse httpResponse = client.get("/" + LIMITED_USER_INDEX + "/_doc/" + ID_1);

httpResponse.assertStatusCode(200);
}
}

@Test
public void shouldAccessIndexWithPlaceholder_negative() {
try(TestRestClient client = cluster.getRestClient(LIMITED_USER)) {
HttpResponse httpResponse = client.get("/" + PROHIBITED_INDEX + "/_doc/" + ID_2);

httpResponse.assertStatusCode(403);
}
}

@Test
public void shouldUseSecurityAdminTool() throws Exception {
SecurityAdminLauncher securityAdminLauncher = new SecurityAdminLauncher(cluster.getHttpPort(), cluster.getTestCertificates());
File rolesMapping = configurationDirectory.newFile("roles_mapping.yml");
ConfigurationFiles.createRoleMappingFile(rolesMapping);

int exitCode = securityAdminLauncher.updateRoleMappings(rolesMapping);

assertThat(exitCode, equalTo(0));
try(TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
Awaitility.await().alias("Waiting for rolemapping 'readall' availability.")
.until(() -> client.get("_plugins/_security/api/rolesmapping/readall").getStatusCode(), equalTo(200));
}
}
}
Loading

0 comments on commit 079d7f0

Please sign in to comment.