-
Notifications
You must be signed in to change notification settings - Fork 281
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test related to security plugin configuration updates.
Signed-off-by: Lukasz Soszynski <[email protected]>
- Loading branch information
1 parent
16b1676
commit 079d7f0
Showing
21 changed files
with
669 additions
and
61 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
src/integrationTest/java/org/opensearch/security/ConfigurationFiles.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/integrationTest/java/org/opensearch/security/DefaultConfigurationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/integrationTest/java/org/opensearch/security/SecurityAdminLauncher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
223 changes: 223 additions & 0 deletions
223
src/integrationTest/java/org/opensearch/security/SecurityConfigurationTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} | ||
} |
Oops, something went wrong.