Skip to content

Commit

Permalink
Moved tests into integration tests
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Pleskach <[email protected]>
  • Loading branch information
willyborankin committed Mar 21, 2024
1 parent dd119e5 commit d194185
Show file tree
Hide file tree
Showing 20 changed files with 1,148 additions and 705 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;

class ConfigurationFiles {
import org.apache.commons.lang3.RandomStringUtils;

public static void createRoleMappingFile(File destination) {
String resource = "roles_mapping.yml";
copyResourceToFile(resource, destination);
}
import org.opensearch.core.common.Strings;
import org.opensearch.security.securityconf.impl.CType;

public class ConfigurationFiles {

public static Path createConfigurationDirectory() {
try {
Path tempDirectory = Files.createTempDirectory("test-security-config");
Path tempDirectory = Files.createTempDirectory("test-security-config-" + RandomStringUtils.random(10));
String[] configurationFiles = {
"config.yml",
"action_groups.yml",
Expand All @@ -48,6 +50,35 @@ public static Path createConfigurationDirectory() {
}
}

public static void writeToConfig(final CType cType, final Path configFolder, final String content) throws IOException {
if (Strings.isNullOrEmpty(content)) return;
final Path configFile = resolveFileFor(cType, configFolder);
try (final var out = Files.newOutputStream(configFile, StandardOpenOption.APPEND)) {
out.write(content.getBytes(StandardCharsets.UTF_8));
out.flush();
}
}

private static Path resolveFileFor(final CType cType, final Path configFolder) {
switch (cType) {
case ACTIONGROUPS:
return configFolder.resolve("action_groups.yml");
case INTERNALUSERS:
return configFolder.resolve("internal_users.yml");
case ROLES:
return configFolder.resolve("roles.yml");
case ROLESMAPPING:
return configFolder.resolve("roles_mapping.yml");
default:
throw new IllegalArgumentException("Unsupported configuration type: " + cType);
}
}

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

private static void copyResourceToFile(String resource, File destination) {
try (InputStream input = ConfigurationFiles.class.getClassLoader().getResourceAsStream(resource)) {
Objects.requireNonNull(input, "Cannot find source resource " + resource);
Expand Down
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;
}

}
Loading

0 comments on commit d194185

Please sign in to comment.