-
Notifications
You must be signed in to change notification settings - Fork 282
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Andrey Pleskach <[email protected]>
- Loading branch information
1 parent
dd119e5
commit d194185
Showing
20 changed files
with
1,148 additions
and
705 deletions.
There are no files selected for viewing
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
300 changes: 300 additions & 0 deletions
300
src/integrationTest/java/org/opensearch/security/api/AbstractApiIntegrationTest.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,300 @@ | ||
/* | ||
* Copyright 2015-2017 floragunn GmbH | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
/* | ||
* 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. | ||
* | ||
* Modifications Copyright OpenSearch Contributors. See | ||
* GitHub history for details. | ||
*/ | ||
package org.opensearch.security.api; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.StringJoiner; | ||
|
||
import com.carrotsearch.randomizedtesting.RandomizedTest; | ||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; | ||
import com.google.common.collect.ImmutableMap; | ||
import com.fasterxml.jackson.annotation.JsonIgnore; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.node.ObjectNode; | ||
import org.apache.commons.io.FileUtils; | ||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
import org.awaitility.Awaitility; | ||
import org.junit.AfterClass; | ||
import org.junit.BeforeClass; | ||
import org.junit.runner.RunWith; | ||
|
||
import org.opensearch.common.CheckedConsumer; | ||
import org.opensearch.common.xcontent.XContentFactory; | ||
import org.opensearch.core.xcontent.ToXContent; | ||
import org.opensearch.security.ConfigurationFiles; | ||
import org.opensearch.security.securityconf.impl.CType; | ||
import org.opensearch.test.framework.TestSecurityConfig; | ||
import org.opensearch.test.framework.certificate.CertificateData; | ||
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; | ||
import static org.opensearch.security.CrossClusterSearchTests.PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED; | ||
import static org.opensearch.security.DefaultObjectMapper.objectMapper; | ||
import static org.opensearch.security.support.ConfigConstants.SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX; | ||
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ADMIN_ENABLED; | ||
|
||
@ThreadLeakScope(ThreadLeakScope.Scope.NONE) | ||
@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) | ||
public abstract class AbstractApiIntegrationTest extends RandomizedTest { | ||
|
||
private static final Logger LOGGER = LogManager.getLogger(TestSecurityConfig.class); | ||
|
||
public static final String NEW_USER = "new-user"; | ||
|
||
public static final String REST_ADMIN_USER = "rest-api-admin"; | ||
|
||
public static final String ADMIN_USER_NAME = "admin"; | ||
|
||
public static final String DEFAULT_PASSWORD = "secret"; | ||
|
||
public static final String EMPTY_BODY = "{}"; | ||
|
||
public final static Path CONFIGURATION_FOLDER = ConfigurationFiles.createConfigurationDirectory(); | ||
|
||
public static Map<String, Object> clusterSettings; | ||
|
||
public static Set<TestSecurityConfig.User> users; | ||
|
||
public static Set<TestSecurityConfig.Role> roles; | ||
|
||
public static Set<TestSecurityConfig.RoleMapping> rolesMappings; | ||
|
||
public static Set<TestSecurityConfig.ActionGroup> actionGroups; | ||
|
||
public static LocalCluster localCluster; | ||
|
||
static class Patch { | ||
|
||
enum Op { | ||
ADD, | ||
REPLACE, | ||
REMOVE; | ||
} | ||
|
||
final String path; | ||
|
||
final Op op; | ||
|
||
ObjectNode value; | ||
|
||
private Patch(Op op, String path, ObjectNode value) { | ||
this.path = path; | ||
this.op = op; | ||
this.value = value; | ||
} | ||
|
||
@JsonIgnore | ||
static Patch add(String path, ObjectNode value) { | ||
return new Patch(Op.ADD, path, value); | ||
} | ||
|
||
@JsonIgnore | ||
static Patch replace(String path, ObjectNode value) { | ||
return new Patch(Op.REPLACE, path, value); | ||
} | ||
|
||
@JsonIgnore | ||
static Patch remove(String path) { | ||
return new Patch(Op.REMOVE, path, null); | ||
} | ||
|
||
} | ||
|
||
@BeforeClass | ||
public static void startCluster() throws IOException { | ||
final var nodeSettings = ImmutableMap.<String, Object>builder() | ||
.put(SECURITY_ALLOW_DEFAULT_INIT_SECURITYINDEX, true) | ||
.put(SECURITY_RESTAPI_ADMIN_ENABLED, true) | ||
.put(PLUGINS_SECURITY_RESTAPI_ROLES_ENABLED, List.of("user_admin__all_access")); | ||
if (clusterSettings != null) nodeSettings.putAll(clusterSettings); | ||
final var clusterManager = randomFrom(List.of(ClusterManager.THREE_CLUSTER_MANAGERS, ClusterManager.SINGLENODE)); | ||
final var localClusterBuilder = new LocalCluster.Builder().clusterManager(clusterManager) | ||
.nodeSettings(nodeSettings.buildKeepingLast()) | ||
.defaultConfigurationInitDirectory(CONFIGURATION_FOLDER.toString()) | ||
.loadConfigurationIntoIndex(false); | ||
extendConfiguration(); | ||
localCluster = localClusterBuilder.build(); | ||
localCluster.before(); | ||
try (TestRestClient client = localCluster.getRestClient(ADMIN_USER_NAME, DEFAULT_PASSWORD)) { | ||
Awaitility.await().alias("Load default configuration").until(() -> client.getAuthInfo().getStatusCode(), equalTo(200)); | ||
} | ||
} | ||
|
||
private static void extendConfiguration() throws IOException { | ||
extendActionGroups(CONFIGURATION_FOLDER, actionGroups); | ||
extendRoles(CONFIGURATION_FOLDER, roles); | ||
extendRolesMapping(CONFIGURATION_FOLDER, rolesMappings); | ||
extendUsers(CONFIGURATION_FOLDER, users); | ||
} | ||
|
||
private static void extendUsers(final Path configFolder, final Set<TestSecurityConfig.User> users) throws IOException { | ||
if (users == null) return; | ||
if (users.isEmpty()) return; | ||
LOGGER.info("Adding users to the default configuration: "); | ||
try (final var contentBuilder = XContentFactory.yamlBuilder()) { | ||
contentBuilder.startObject(); | ||
for (final var u : users) { | ||
LOGGER.info("\t\t - {}", u.getName()); | ||
contentBuilder.field(u.getName()); | ||
u.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); | ||
} | ||
contentBuilder.endObject(); | ||
ConfigurationFiles.writeToConfig(CType.INTERNALUSERS, configFolder, removeDashes(contentBuilder.toString())); | ||
} | ||
} | ||
|
||
private static void extendActionGroups(final Path configFolder, final Set<TestSecurityConfig.ActionGroup> actionGroups) | ||
throws IOException { | ||
if (actionGroups == null) return; | ||
if (actionGroups.isEmpty()) return; | ||
LOGGER.info("Adding action groups to the default configuration: "); | ||
try (final var contentBuilder = XContentFactory.yamlBuilder()) { | ||
contentBuilder.startObject(); | ||
for (final var ag : actionGroups) { | ||
LOGGER.info("\t\t - {}", ag.name()); | ||
contentBuilder.field(ag.name()); | ||
ag.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); | ||
} | ||
contentBuilder.endObject(); | ||
ConfigurationFiles.writeToConfig(CType.ACTIONGROUPS, configFolder, removeDashes(contentBuilder.toString())); | ||
} | ||
} | ||
|
||
private static void extendRoles(final Path configFolder, final Set<TestSecurityConfig.Role> roles) throws IOException { | ||
if (roles == null) return; | ||
if (roles.isEmpty()) return; | ||
LOGGER.info("Adding roles to the default configuration: "); | ||
try (final var contentBuilder = XContentFactory.yamlBuilder()) { | ||
contentBuilder.startObject(); | ||
for (final var r : roles) { | ||
LOGGER.info("\t\t - {}", r.getName()); | ||
contentBuilder.field(r.getName()); | ||
r.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); | ||
} | ||
contentBuilder.endObject(); | ||
ConfigurationFiles.writeToConfig(CType.ROLES, configFolder, removeDashes(contentBuilder.toString())); | ||
} | ||
} | ||
|
||
private static void extendRolesMapping(final Path configFolder, final Set<TestSecurityConfig.RoleMapping> rolesMapping) | ||
throws IOException { | ||
if (rolesMapping == null) return; | ||
if (rolesMapping.isEmpty()) return; | ||
LOGGER.info("Adding roles mapping to the default configuration: "); | ||
try (final var contentBuilder = XContentFactory.yamlBuilder()) { | ||
contentBuilder.startObject(); | ||
for (final var rm : rolesMapping) { | ||
LOGGER.info("\t\t - {}", rm.name()); | ||
contentBuilder.field(rm.name()); | ||
rm.toXContent(contentBuilder, ToXContent.EMPTY_PARAMS); | ||
} | ||
contentBuilder.endObject(); | ||
ConfigurationFiles.writeToConfig(CType.ROLESMAPPING, configFolder, removeDashes(contentBuilder.toString())); | ||
} | ||
} | ||
|
||
private static String removeDashes(final String content) { | ||
return content.replace("---", ""); | ||
} | ||
|
||
@AfterClass | ||
public static void stopCluster() throws IOException { | ||
if (localCluster != null) localCluster.close(); | ||
FileUtils.deleteDirectory(CONFIGURATION_FOLDER.toFile()); | ||
} | ||
|
||
protected void withUser(final String user, final CheckedConsumer<TestRestClient, Exception> restClientHandler) throws Exception { | ||
withUser(user, DEFAULT_PASSWORD, restClientHandler); | ||
} | ||
|
||
protected void withUser(final String user, final String password, final CheckedConsumer<TestRestClient, Exception> restClientHandler) | ||
throws Exception { | ||
try (TestRestClient client = localCluster.getRestClient(user, password)) { | ||
restClientHandler.accept(client); | ||
} | ||
} | ||
|
||
protected void withUser( | ||
final String user, | ||
final CertificateData certificateData, | ||
final CheckedConsumer<TestRestClient, Exception> restClientHandler | ||
) throws Exception { | ||
withUser(user, DEFAULT_PASSWORD, certificateData, restClientHandler); | ||
} | ||
|
||
protected void withUser( | ||
final String user, | ||
final String password, | ||
final CertificateData certificateData, | ||
final CheckedConsumer<TestRestClient, Exception> restClientHandler | ||
) throws Exception { | ||
try (TestRestClient client = localCluster.getRestClient(user, password, certificateData)) { | ||
restClientHandler.accept(client); | ||
} | ||
} | ||
|
||
protected String securityPath(String... path) { | ||
final var fullPath = new StringJoiner("/"); | ||
fullPath.add(randomFrom(List.of("_opendistro/_security", "_plugins/_security"))); | ||
if (path != null) { | ||
for (final var p : path) | ||
fullPath.add(p); | ||
} | ||
return fullPath.toString(); | ||
} | ||
|
||
protected String api() { | ||
return String.format("%s/api", securityPath()); | ||
} | ||
|
||
protected String apiPath(final String... path) { | ||
|
||
final var fullPath = new StringJoiner("/"); | ||
fullPath.add(api()); | ||
|
||
for (final var p : path) { | ||
fullPath.add(p); | ||
} | ||
return fullPath.toString(); | ||
} | ||
|
||
JsonNode patchPayload(final Patch... patch) { | ||
final var arr = objectMapper.createArrayNode(); | ||
for (final var p : patch) | ||
arr.add(objectMapper.valueToTree(p)); | ||
return arr; | ||
} | ||
|
||
} |
Oops, something went wrong.