Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,10 @@

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;

import org.opensearch.core.common.Strings;
import org.opensearch.security.securityconf.impl.CType;

public class ConfigurationFiles {
Expand All @@ -43,14 +40,6 @@ public static Path createConfigurationDirectory() {
}
}

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

public static void copyResourceToFile(String resource, Path destination) {
try (InputStream input = ConfigurationFiles.class.getClassLoader().getResourceAsStream(resource)) {
Objects.requireNonNull(input, "Cannot find source resource " + resource);
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,34 @@
import java.util.StringJoiner;

import org.hamcrest.Matcher;
import org.junit.Test;

import org.opensearch.common.CheckedSupplier;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.test.framework.cluster.LocalCluster;
import org.opensearch.test.framework.cluster.TestRestClient;

import com.nimbusds.jose.util.Pair;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.oneOf;
import static org.opensearch.security.api.PatchPayloadHelper.addOp;
import static org.opensearch.security.api.PatchPayloadHelper.patch;
import static org.opensearch.security.api.PatchPayloadHelper.removeOp;
import static org.opensearch.security.api.PatchPayloadHelper.replaceOp;
import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ADMIN_ENABLED;
import static org.opensearch.test.framework.matcher.RestMatchers.isBadRequest;
import static org.opensearch.test.framework.matcher.RestMatchers.isCreated;
import static org.opensearch.test.framework.matcher.RestMatchers.isForbidden;
import static org.opensearch.test.framework.matcher.RestMatchers.isNotFound;
import static org.opensearch.test.framework.matcher.RestMatchers.isOk;

public abstract class AbstractConfigEntityApiIntegrationTest extends AbstractApiIntegrationTest {

static {
testSecurityConfig.withRestAdminUser(REST_ADMIN_USER, allRestAdminPermissions());
}

@Override
protected Map<String, Object> getClusterSettings() {
Map<String, Object> clusterSettings = super.getClusterSettings();
clusterSettings.put(SECURITY_RESTAPI_ADMIN_ENABLED, true);
return clusterSettings;
protected static LocalCluster.Builder clusterBuilder() {
return AbstractApiIntegrationTest.clusterBuilder().nodeSetting(SECURITY_RESTAPI_ADMIN_ENABLED, true);
}

interface TestDescriptor {
Expand Down Expand Up @@ -99,26 +96,24 @@ protected String apiPath(String... paths) {
return fullPath.toString();
}

@Test
public void forbiddenForRegularUsers() throws Exception {
withUser(NEW_USER, client -> {
forbidden(() -> client.putJson(apiPath("some_entity"), EMPTY_BODY));
forbidden(() -> client.get(apiPath()));
forbidden(() -> client.get(apiPath("some_entity")));
forbidden(() -> client.putJson(apiPath("some_entity"), EMPTY_BODY));
forbidden(() -> client.patch(apiPath(), EMPTY_BODY));
forbidden(() -> client.patch(apiPath("some_entity"), EMPTY_BODY));
forbidden(() -> client.delete(apiPath("some_entity")));
});
public void forbiddenForRegularUsers(LocalCluster localCluster) throws Exception {
try (TestRestClient client = localCluster.getRestClient(NEW_USER)) {
assertThat(client.putJson(apiPath("some_entity"), EMPTY_BODY), isForbidden());
assertThat(client.get(apiPath()), isForbidden());
assertThat(client.get(apiPath("some_entity")), isForbidden());
assertThat(client.putJson(apiPath("some_entity"), EMPTY_BODY), isForbidden());
assertThat(client.patch(apiPath(), EMPTY_BODY), isForbidden());
assertThat(client.patch(apiPath("some_entity"), EMPTY_BODY), isForbidden());
assertThat(client.delete(apiPath("some_entity")), isForbidden());
}
}

@Test
public void availableForAdminUser() throws Exception {
final var entitiesNames = predefinedHiddenAndReservedConfigEntities();
public void availableForAdminUser(LocalCluster localCluster) throws Exception {
final var entitiesNames = predefinedHiddenAndReservedConfigEntities(localCluster);
final var hiddenEntityName = entitiesNames.getLeft();
final var reservedEntityName = entitiesNames.getRight();
// can't see hidden resources
withUser(ADMIN_USER_NAME, client -> {
try (TestRestClient client = localCluster.getRestClient(ADMIN_USER)) {
verifyNoHiddenEntities(() -> client.get(apiPath()));
creationOfReadOnlyEntityForbidden(
randomAsciiAlphanumOfLength(10),
Expand All @@ -131,35 +126,28 @@ public void availableForAdminUser() throws Exception {
verifyUpdateAndDeleteReservedConfigEntityForbidden(reservedEntityName, client);
verifyCrudOperations(null, null, client);
verifyBadRequestOperations(client);
});
}
}

Pair<String, String> predefinedHiddenAndReservedConfigEntities() throws Exception {
Pair<String, String> predefinedHiddenAndReservedConfigEntities(LocalCluster localCluster) throws Exception {
final var hiddenEntityName = randomAsciiAlphanumOfLength(10);
final var reservedEntityName = randomAsciiAlphanumOfLength(10);
withUser(
ADMIN_USER_NAME,
localCluster.getAdminCertificate(),
client -> created(() -> client.putJson(apiPath(hiddenEntityName), testDescriptor.hiddenEntityPayload()))
);
withUser(
ADMIN_USER_NAME,
localCluster.getAdminCertificate(),
client -> created(() -> client.putJson(apiPath(reservedEntityName), testDescriptor.reservedEntityPayload()))
);
try (TestRestClient client = localCluster.getAdminCertRestClient()) {
assertThat(client.putJson(apiPath(hiddenEntityName), testDescriptor.hiddenEntityPayload()), isCreated());
assertThat(client.putJson(apiPath(reservedEntityName), testDescriptor.reservedEntityPayload()), isCreated());
}
return Pair.of(hiddenEntityName, reservedEntityName);
}

@Test
public void availableForTLSAdminUser() throws Exception {
withUser(ADMIN_USER_NAME, localCluster.getAdminCertificate(), this::availableForSuperAdminUser);
public void availableForTLSAdminUser(LocalCluster localCluster) throws Exception {
try (TestRestClient client = localCluster.getAdminCertRestClient()) {
availableForSuperAdminUser(client);
}
}

@Test
public void availableForRESTAdminUser() throws Exception {
withUser(REST_ADMIN_USER, this::availableForSuperAdminUser);
if (testDescriptor.restAdminLimitedUser().isPresent()) {
withUser(testDescriptor.restAdminLimitedUser().get(), this::availableForSuperAdminUser);
public void availableForRESTAdminUser(LocalCluster localCluster) throws Exception {
try (TestRestClient client = localCluster.getRestClient(REST_ADMIN_USER)) {
availableForSuperAdminUser(client);
}
}

Expand All @@ -178,7 +166,9 @@ void availableForSuperAdminUser(final TestRestClient client) throws Exception {
}

void verifyNoHiddenEntities(final CheckedSupplier<TestRestClient.HttpResponse, Exception> endpointCallback) throws Exception {
final var body = ok(endpointCallback).bodyAsJsonNode();
final var resp = endpointCallback.get();
assertThat(resp, isOk());
final var body = resp.bodyAsJsonNode();
final var pretty = body.toPrettyString();
final var it = body.elements();
while (it.hasNext()) {
Expand All @@ -190,11 +180,11 @@ void verifyNoHiddenEntities(final CheckedSupplier<TestRestClient.HttpResponse, E
void creationOfReadOnlyEntityForbidden(final String entityName, final TestRestClient client, final ToXContentObject... entities)
throws Exception {
for (final var configEntity : entities) {
assertInvalidKeys(
badRequest(() -> client.putJson(apiPath(entityName), configEntity)),
is(oneOf("static", "hidden", "reserved"))
);
badRequest(() -> client.patch(apiPath(), patch(addOp(randomAsciiAlphanumOfLength(10), configEntity))));
final var resp = client.putJson(apiPath(entityName), configEntity);
assertThat(resp, isBadRequest());
assertInvalidKeys(resp, is(oneOf("static", "hidden", "reserved")));
final var resp2 = client.patch(apiPath(), patch(addOp(randomAsciiAlphanumOfLength(10), configEntity)));
assertThat(resp2, isBadRequest());
}
}

Expand Down Expand Up @@ -222,54 +212,60 @@ void assertWrongDataType(final TestRestClient.HttpResponse response, final Map<S
assertThat(response.getBody(), response.getTextFromJsonBody("/" + p.getKey()), is(p.getValue()));
}

void assertSpecifyOneOf(final TestRestClient.HttpResponse response, final String expectedSpecifyOneOfKeys) {
assertThat(response.getBody(), response.getTextFromJsonBody("/status"), is("error"));
assertThat(response.getBody(), response.getTextFromJsonBody("/reason"), equalTo("Invalid configuration"));
assertThat(response.getBody(), response.getTextFromJsonBody("/specify_one_of/keys"), containsString(expectedSpecifyOneOfKeys));
}

void assertMissingMandatoryKeys(final TestRestClient.HttpResponse response, final String expectedKeys) {
assertThat(response.getBody(), response.getTextFromJsonBody("/status"), is("error"));
assertThat(response.getBody(), response.getTextFromJsonBody("/reason"), equalTo("Invalid configuration"));
assertThat(response.getBody(), response.getTextFromJsonBody("/missing_mandatory_keys/keys"), containsString(expectedKeys));
}

void verifyUpdateAndDeleteHiddenConfigEntityForbidden(final String hiddenEntityName, final TestRestClient client) throws Exception {
final var expectedErrorMessage = "Resource '" + hiddenEntityName + "' is not available.";
notFound(() -> client.putJson(apiPath(hiddenEntityName), testDescriptor.entityPayload()), expectedErrorMessage);
notFound(
() -> client.patch(
assertThat(
client.putJson(apiPath(hiddenEntityName), testDescriptor.entityPayload()),
isNotFound().withAttribute("/message", expectedErrorMessage)
);
assertThat(
client.patch(
apiPath(hiddenEntityName),
patch(replaceOp(testDescriptor.entityJsonProperty(), testDescriptor.jsonPropertyPayload()))
),
expectedErrorMessage
isNotFound().withAttribute("/message", expectedErrorMessage)
);
notFound(() -> client.patch(apiPath(), patch(replaceOp(hiddenEntityName, testDescriptor.entityPayload()))), expectedErrorMessage);
notFound(() -> client.patch(apiPath(hiddenEntityName), patch(removeOp(testDescriptor.entityJsonProperty()))), expectedErrorMessage);
notFound(() -> client.patch(apiPath(), patch(removeOp(hiddenEntityName))), expectedErrorMessage);
notFound(() -> client.delete(apiPath(hiddenEntityName)), expectedErrorMessage);
assertThat(
client.patch(apiPath(), patch(replaceOp(hiddenEntityName, testDescriptor.entityPayload()))),
isNotFound().withAttribute("/message", expectedErrorMessage)
);
assertThat(
client.patch(apiPath(hiddenEntityName), patch(removeOp(testDescriptor.entityJsonProperty()))),
isNotFound().withAttribute("/message", expectedErrorMessage)
);
assertThat(
client.patch(apiPath(), patch(removeOp(hiddenEntityName))),
isNotFound().withAttribute("/message", expectedErrorMessage)
);
assertThat(client.delete(apiPath(hiddenEntityName)), isNotFound().withAttribute("/message", expectedErrorMessage));
}

void verifyUpdateAndDeleteReservedConfigEntityForbidden(final String reservedEntityName, final TestRestClient client) throws Exception {
final var expectedErrorMessage = "Resource '" + reservedEntityName + "' is reserved.";
forbidden(() -> client.putJson(apiPath(reservedEntityName), testDescriptor.entityPayload()), expectedErrorMessage);
forbidden(
() -> client.patch(
assertThat(
client.putJson(apiPath(reservedEntityName), testDescriptor.entityPayload()),
isForbidden().withAttribute("/message", expectedErrorMessage)
);
assertThat(
client.patch(
apiPath(reservedEntityName),
patch(replaceOp(testDescriptor.entityJsonProperty(), testDescriptor.entityJsonProperty()))
),
expectedErrorMessage
isForbidden().withAttribute("/message", expectedErrorMessage)
);
forbidden(
() -> client.patch(apiPath(), patch(replaceOp(reservedEntityName, testDescriptor.entityPayload()))),
expectedErrorMessage
assertThat(
client.patch(apiPath(), patch(replaceOp(reservedEntityName, testDescriptor.entityPayload()))),
isForbidden().withAttribute("/message", expectedErrorMessage)
);
assertThat(
client.patch(apiPath(), patch(removeOp(reservedEntityName))),
isForbidden().withAttribute("/message", expectedErrorMessage)
);
forbidden(() -> client.patch(apiPath(), patch(removeOp(reservedEntityName))), expectedErrorMessage);
forbidden(
() -> client.patch(apiPath(reservedEntityName), patch(removeOp(testDescriptor.entityJsonProperty()))),
expectedErrorMessage
assertThat(
client.patch(apiPath(reservedEntityName), patch(removeOp(testDescriptor.entityJsonProperty()))),
isForbidden().withAttribute("/message", expectedErrorMessage)
);
forbidden(() -> client.delete(apiPath(reservedEntityName)), expectedErrorMessage);
assertThat(client.delete(apiPath(reservedEntityName)), isForbidden().withAttribute("/message", expectedErrorMessage));
}

void forbiddenToCreateEntityWithRestAdminPermissions(final TestRestClient client) throws Exception {}
Expand Down
Loading
Loading