From 1f5bd36cd1ea9d373472d0749dde9b0451020b8d Mon Sep 17 00:00:00 2001 From: Andrey Pleskach Date: Thu, 20 Jun 2024 17:10:37 +0200 Subject: [PATCH] Add Tenants REST API test and partial fix #4166 (#4465) Signed-off-by: Andrey Pleskach --- .../api/TenantsRestApiIntegrationTest.java | 186 ++++++++++++++++++ .../dlic/rest/api/TenantsApiAction.java | 1 + 2 files changed, 187 insertions(+) create mode 100644 src/integrationTest/java/org/opensearch/security/api/TenantsRestApiIntegrationTest.java diff --git a/src/integrationTest/java/org/opensearch/security/api/TenantsRestApiIntegrationTest.java b/src/integrationTest/java/org/opensearch/security/api/TenantsRestApiIntegrationTest.java new file mode 100644 index 0000000000..cb3431be79 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/api/TenantsRestApiIntegrationTest.java @@ -0,0 +1,186 @@ +/* + * 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.util.Optional; + +import com.fasterxml.jackson.databind.JsonNode; + +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.security.dlic.rest.api.Endpoint; +import org.opensearch.test.framework.cluster.TestRestClient; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +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; + +public class TenantsRestApiIntegrationTest extends AbstractConfigEntityApiIntegrationTest { + + private final static String REST_API_ADMIN_TENANTS_ONLY = "rest_api_admin_tenants_only"; + + static { + testSecurityConfig.withRestAdminUser(REST_API_ADMIN_TENANTS_ONLY, restAdminPermission(Endpoint.TENANTS)); + } + + public TenantsRestApiIntegrationTest() { + super("tenants", new TestDescriptor() { + @Override + public String entityJsonProperty() { + return "description"; + } + + @Override + public ToXContentObject entityPayload(Boolean hidden, Boolean reserved, Boolean _static) { + return tenant(hidden, reserved, _static); + } + + @Override + public ToXContentObject jsonPropertyPayload() { + return (builder, params) -> builder.value(randomAsciiAlphanumOfLength(10)); + } + + @Override + public Optional restAdminLimitedUser() { + return Optional.of(REST_API_ADMIN_TENANTS_ONLY); + } + }); + } + + static ToXContentObject tenant(final Boolean hidden, final Boolean reserved, final String description) { + return tenant(hidden, reserved, null, description); + } + + static ToXContentObject tenant(final Boolean hidden, final Boolean reserved, final Boolean _static) { + return tenant(hidden, reserved, _static, randomAsciiAlphanumOfLength(10)); + } + + static ToXContentObject tenant(final Boolean hidden, final Boolean reserved, final Boolean _static, String description) { + return (builder, params) -> { + builder.startObject(); + if (hidden != null) { + builder.field("hidden", hidden); + } + if (reserved != null) { + builder.field("reserved", reserved); + } + if (_static != null) { + builder.field("static", _static); + } + builder.field("description", description); + return builder.endObject(); + }; + } + + @Override + void verifyBadRequestOperations(TestRestClient client) throws Exception { + // put + badRequest(() -> client.putJson(apiPath(randomAsciiAlphanumOfLength(4)), EMPTY_BODY)); + badRequest( + () -> client.putJson( + apiPath(randomAsciiAlphanumOfLength(4)), + (builder, params) -> builder.startObject().field("description", "a").field("description", "b").endObject() + ) + ); + assertInvalidKeys( + client.putJson( + apiPath(randomAsciiAlphanumOfLength(10)), + (builder, params) -> builder.startObject().field("a", "b").field("c", "d").field("description", "e").endObject() + ), + "a,c" + ); + // patch + badRequest(() -> client.patch(apiPath(), EMPTY_BODY)); + badRequest( + () -> client.patch( + apiPath(), + patch( + addOp( + randomAsciiAlphanumOfLength(4), + (ToXContentObject) (builder, params) -> builder.startObject() + .field("description", "a") + .field("description", "b") + .endObject() + ) + ) + ) + ); + assertInvalidKeys( + client.patch( + apiPath(), + patch( + addOp( + randomAsciiAlphanumOfLength(10), + (ToXContentObject) (builder, params) -> builder.startObject() + .field("a", "b") + .field("c", "d") + .field("description", "e") + .endObject() + ) + ) + ), + "a,c" + ); + + } + + @Override + void verifyCrudOperations(Boolean hidden, Boolean reserved, TestRestClient client) throws Exception { + // put + final var putDescription = randomAsciiAlphanumOfLength(10); + final var putTenantName = randomAsciiAlphanumOfLength(4); + created(() -> client.putJson(apiPath(putTenantName), tenant(hidden, reserved, putDescription))); + assertTenant(ok(() -> client.get(apiPath(putTenantName))).bodyAsJsonNode().get(putTenantName), hidden, reserved, putDescription); + + final var putUpdatedDescription = randomAsciiAlphanumOfLength(10); + ok(() -> client.putJson(apiPath(putTenantName), tenant(hidden, reserved, putUpdatedDescription))); + assertTenant( + ok(() -> client.get(apiPath(putTenantName))).bodyAsJsonNode().get(putTenantName), + hidden, + reserved, + putUpdatedDescription + ); + ok(() -> client.delete(apiPath(putTenantName))); + notFound(() -> client.get(apiPath(putTenantName))); + // patch + final var patchTenantName = randomAsciiAlphanumOfLength(4); + final var patchDescription = randomAsciiAlphanumOfLength(10); + ok(() -> client.patch(apiPath(), patch(addOp(patchTenantName, tenant(hidden, reserved, patchDescription))))); + assertTenant( + ok(() -> client.get(apiPath(patchTenantName))).bodyAsJsonNode().get(patchTenantName), + hidden, + reserved, + patchDescription + ); + + final var patchUpdatedDescription = randomAsciiAlphanumOfLength(10); + ok(() -> client.patch(apiPath(patchTenantName), patch(replaceOp("description", patchUpdatedDescription)))); + assertTenant( + ok(() -> client.get(apiPath(patchTenantName))).bodyAsJsonNode().get(patchTenantName), + hidden, + reserved, + patchUpdatedDescription + ); + + ok(() -> client.patch(apiPath(), patch(removeOp(patchTenantName)))); + notFound(() -> client.get(apiPath(patchTenantName))); + } + + void assertTenant(final JsonNode actualJson, final Boolean hidden, final Boolean reserved, final String expectedDescription) { + assertThat(actualJson.toPrettyString(), actualJson.get("hidden").asBoolean(), is(hidden != null && hidden)); + assertThat(actualJson.toPrettyString(), actualJson.get("reserved").asBoolean(), is(reserved != null && reserved)); + assertThat(actualJson.toPrettyString(), actualJson.get("description").asText(), is(expectedDescription)); + } + +} diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java index e16d31ba6f..df2289b44c 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/TenantsApiAction.java @@ -108,6 +108,7 @@ public Settings settings() { public Map allowedKeys() { final ImmutableMap.Builder allowedKeys = ImmutableMap.builder(); if (isCurrentUserAdmin()) { + allowedKeys.put("hidden", DataType.BOOLEAN); allowedKeys.put("reserved", DataType.BOOLEAN); } return allowedKeys.put("description", DataType.STRING).build();