diff --git a/src/integrationTest/java/org/opensearch/security/SecurityConfigurationTests.java b/src/integrationTest/java/org/opensearch/security/SecurityConfigurationTests.java index 73c55bc667..dc2c82c188 100644 --- a/src/integrationTest/java/org/opensearch/security/SecurityConfigurationTests.java +++ b/src/integrationTest/java/org/opensearch/security/SecurityConfigurationTests.java @@ -93,6 +93,11 @@ public static void initData() { 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(); } + try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) { + Awaitility.await() + .alias("Load default configuration") + .until(() -> client.securityHealth().getTextFromJsonBody("/status"), equalTo("UP")); + } } @Test @@ -258,7 +263,11 @@ public void testParallelTenantPutRequests() throws Exception { AtomicInteger numCreatedResponses = new AtomicInteger(); AsyncActions.getAll(conflictingRequests, 1, TimeUnit.SECONDS).forEach((response) -> { - assertThat(response.getStatusCode(), anyOf(equalTo(HttpStatus.SC_CREATED), equalTo(HttpStatus.SC_CONFLICT))); + assertThat( + response.getBody(), + response.getStatusCode(), + anyOf(equalTo(HttpStatus.SC_CREATED), equalTo(HttpStatus.SC_CONFLICT)) + ); if (response.getStatusCode() == HttpStatus.SC_CREATED) numCreatedResponses.getAndIncrement(); }); assertThat(numCreatedResponses.get(), equalTo(1)); // should only be one 201 diff --git a/src/integrationTest/java/org/opensearch/security/api/AbstractConfigEntityApiIntegrationTest.java b/src/integrationTest/java/org/opensearch/security/api/AbstractConfigEntityApiIntegrationTest.java index dda5209942..c852ecb272 100644 --- a/src/integrationTest/java/org/opensearch/security/api/AbstractConfigEntityApiIntegrationTest.java +++ b/src/integrationTest/java/org/opensearch/security/api/AbstractConfigEntityApiIntegrationTest.java @@ -11,6 +11,7 @@ package org.opensearch.security.api; +import java.util.Map; import java.util.Optional; import java.util.StringJoiner; @@ -79,6 +80,33 @@ public AbstractConfigEntityApiIntegrationTest(final String path, final TestDescr this.testDescriptor = testDescriptor; } + static ToXContentObject configJsonArray(final String... values) { + return (builder, params) -> { + builder.startArray(); + if (values != null) { + for (final var v : values) { + if (v == null) { + builder.nullValue(); + } else { + builder.value(v); + } + } + } + return builder.endArray(); + }; + } + + static String[] generateArrayValues(boolean useNulls) { + final var length = randomIntBetween(1, 5); + final var values = new String[length]; + final var nullIndex = randomIntBetween(0, length - 1); + for (var i = 0; i < values.length; i++) { + if (useNulls && i == nullIndex) values[i] = null; + else values[i] = randomAsciiAlphanumOfLength(10); + } + return values; + } + @Override protected String apiPath(String... paths) { final StringJoiner fullPath = new StringJoiner("/").add(super.apiPath(path)); @@ -191,6 +219,12 @@ void assertInvalidKeys(final TestRestClient.HttpResponse response, final Matcher assertThat(response.getBody(), response.getTextFromJsonBody("/invalid_keys/keys"), expectedInvalidKeysMatcher); } + void assertWrongDataType(final TestRestClient.HttpResponse response, final Map expectedMessages) { + assertThat(response.getBody(), response.getTextFromJsonBody("/status"), is("error")); + for (final var p : expectedMessages.entrySet()) + 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")); diff --git a/src/integrationTest/java/org/opensearch/security/api/ActionGroupsRestApiIntegrationTest.java b/src/integrationTest/java/org/opensearch/security/api/ActionGroupsRestApiIntegrationTest.java index 7207ed0fe4..174c2b4ea6 100644 --- a/src/integrationTest/java/org/opensearch/security/api/ActionGroupsRestApiIntegrationTest.java +++ b/src/integrationTest/java/org/opensearch/security/api/ActionGroupsRestApiIntegrationTest.java @@ -12,6 +12,7 @@ package org.opensearch.security.api; import java.util.List; +import java.util.Map; import java.util.Optional; import org.opensearch.core.xcontent.ToXContentObject; @@ -59,7 +60,7 @@ public ToXContentObject entityPayload(final Boolean hidden, final Boolean reserv @Override public ToXContentObject jsonPropertyPayload() { - return allowedActionsArray("a", "b", "c"); + return configJsonArray("a", "b", "c"); } @Override @@ -89,7 +90,7 @@ static ToXContentObject actionGroup( builder.field("type", randomType()); if (allowedActions != null) { builder.field("allowed_actions"); - allowedActionsArray(allowedActions).toXContent(builder, params); + configJsonArray(allowedActions).toXContent(builder, params); } else { builder.startArray("allowed_actions").endArray(); } @@ -110,20 +111,6 @@ static String randomType() { return randomFrom(List.of(TestSecurityConfig.ActionGroup.Type.CLUSTER.type(), TestSecurityConfig.ActionGroup.Type.INDEX.type())); } - static ToXContentObject allowedActionsArray(final String... allowedActions) { - return (builder, params) -> { - builder.startArray(); - for (final var allowedAction : allowedActions) { - if (allowedAction == null) { - builder.nullValue(); - } else { - builder.value(allowedAction); - } - } - return builder.endArray(); - }; - } - @Override void forbiddenToCreateEntityWithRestAdminPermissions(final TestRestClient client) throws Exception { forbidden(() -> client.putJson(apiPath("new_rest_admin_action_group"), actionGroup(randomRestAdminPermission()))); @@ -136,10 +123,7 @@ void forbiddenToUpdateAndDeleteExistingEntityWithRestAdminPermissions(final Test forbidden(() -> client.putJson(apiPath(REST_ADMIN_PERMISSION_ACTION_GROUP), actionGroup())); forbidden(() -> client.patch(apiPath(), patch(replaceOp(REST_ADMIN_PERMISSION_ACTION_GROUP, actionGroup("a", "b"))))); forbidden( - () -> client.patch( - apiPath(REST_ADMIN_PERMISSION_ACTION_GROUP), - patch(replaceOp("allowed_actions", allowedActionsArray("c", "d"))) - ) + () -> client.patch(apiPath(REST_ADMIN_PERMISSION_ACTION_GROUP), patch(replaceOp("allowed_actions", configJsonArray("c", "d")))) ); // remove forbidden(() -> client.patch(apiPath(), patch(removeOp(REST_ADMIN_PERMISSION_ACTION_GROUP)))); @@ -161,7 +145,7 @@ void verifyCrudOperations(final Boolean hidden, final Boolean reserved, final Te ok(() -> client.patch(apiPath(), patch(addOp("new_action_group_for_patch", actionGroup(hidden, reserved, "e", "f"))))); assertActionGroup(ok(() -> client.get(apiPath("new_action_group_for_patch"))), "new_action_group_for_patch", List.of("e", "f")); - ok(() -> client.patch(apiPath("new_action_group_for_patch"), patch(replaceOp("allowed_actions", allowedActionsArray("g", "h"))))); + ok(() -> client.patch(apiPath("new_action_group_for_patch"), patch(replaceOp("allowed_actions", configJsonArray("g", "h"))))); assertActionGroup(ok(() -> client.get(apiPath("new_action_group_for_patch"))), "new_action_group_for_patch", List.of("g", "h")); ok(() -> client.patch(apiPath(), patch(removeOp("new_action_group_for_patch")))); @@ -183,32 +167,39 @@ void verifyBadRequestOperations(final TestRestClient client) throws Exception { badRequestWithMessage(() -> client.putJson(apiPath("some_action_group"), (builder, params) -> { builder.startObject().field("type", "asdasdsad").field("allowed_actions"); - allowedActionsArray("g", "f").toXContent(builder, params); + configJsonArray("g", "f").toXContent(builder, params); return builder.endObject(); }), "Invalid action group type: asdasdsad. Supported types are: cluster, index."); assertMissingMandatoryKeys( - badRequest(() -> client.putJson(apiPath("some_action_group"), allowedActionsArray("a", "b", "c"))), + badRequest(() -> client.putJson(apiPath("some_action_group"), configJsonArray("a", "b", "c"))), "allowed_actions" ); assertMissingMandatoryKeys( - badRequest(() -> client.putJson(apiPath("some_action_group"), allowedActionsArray("a", "b", "c"))), + badRequest(() -> client.putJson(apiPath("some_action_group"), configJsonArray("a", "b", "c"))), "allowed_actions" ); final ToXContentObject unknownJsonFields = (builder, params) -> { builder.startObject().field("a", "b").field("c", "d").field("allowed_actions"); - allowedActionsArray("g", "h").toXContent(builder, params); + configJsonArray("g", "h").toXContent(builder, params); return builder.endObject(); }; assertInvalidKeys(badRequest(() -> client.putJson(apiPath("some_action_group"), unknownJsonFields)), "a,c"); assertNullValuesInArray(badRequest(() -> client.putJson(apiPath("some_action_group"), (builder, params) -> { builder.startObject().field("type", randomType()).field("allowed_actions"); - allowedActionsArray("g", null, "f").toXContent(builder, params); + configJsonArray("g", null, "f").toXContent(builder, params); return builder.endObject(); }))); + assertWrongDataType( + client.putJson( + apiPath("some_action_group"), + (builder, params) -> builder.startObject().field("allowed_actions", "a").endObject() + ), + Map.of("allowed_actions", "Array expected") + ); // patch badRequest(() -> client.patch(apiPath("some_action_group"), EMPTY_BODY)); badRequest(() -> client.patch(apiPath(), patch(addOp("some_action_group", EMPTY_BODY)))); @@ -224,12 +215,12 @@ void verifyBadRequestOperations(final TestRestClient client) throws Exception { ); assertMissingMandatoryKeys( - badRequest(() -> client.patch(apiPath(), patch(addOp("some_action_group", allowedActionsArray("a", "b", "c"))))), + badRequest(() -> client.patch(apiPath(), patch(addOp("some_action_group", configJsonArray("a", "b", "c"))))), "allowed_actions" ); badRequest(() -> client.patch(apiPath(), patch(addOp("some_action_group", (ToXContentObject) (builder, params) -> { builder.startObject().field("type", "aaaa").field("allowed_actions"); - allowedActionsArray("g", "f").toXContent(builder, params); + configJsonArray("g", "f").toXContent(builder, params); return builder.endObject(); })))); @@ -241,10 +232,22 @@ void verifyBadRequestOperations(final TestRestClient client) throws Exception { assertNullValuesInArray( badRequest(() -> client.patch(apiPath(), patch(addOp("some_action_group", (ToXContentObject) (builder, params) -> { builder.startObject().field("type", randomType()).field("allowed_actions"); - allowedActionsArray("g", null, "f").toXContent(builder, params); + configJsonArray("g", null, "f").toXContent(builder, params); return builder.endObject(); })))) ); + assertWrongDataType( + client.patch( + apiPath(), + patch( + addOp( + "some_action_group", + (ToXContentObject) (builder, params) -> builder.startObject().field("allowed_actions", "a").endObject() + ) + ) + ), + Map.of("allowed_actions", "Array expected") + ); } void assertActionGroup(final TestRestClient.HttpResponse response, final String actionGroupName, final List allowedActions) { diff --git a/src/integrationTest/java/org/opensearch/security/api/RolesRestApiIntegrationTest.java b/src/integrationTest/java/org/opensearch/security/api/RolesRestApiIntegrationTest.java new file mode 100644 index 0000000000..178436b75a --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/api/RolesRestApiIntegrationTest.java @@ -0,0 +1,462 @@ +/* + * 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.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.core.common.Strings; +import org.opensearch.core.xcontent.ToXContentObject; +import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.dlic.rest.api.Endpoint; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.cluster.TestRestClient; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +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 RolesRestApiIntegrationTest extends AbstractConfigEntityApiIntegrationTest { + + private final static String REST_API_ADMIN_ACTION_ROLES_ONLY = "rest_api_admin_action_roles_only"; + + private final static String REST_ADMIN_PERMISSION_ROLE = "rest-admin-permission-role"; + + static { + testSecurityConfig.withRestAdminUser(REST_API_ADMIN_ACTION_ROLES_ONLY, restAdminPermission(Endpoint.ROLES)) + .roles(new TestSecurityConfig.Role(REST_ADMIN_PERMISSION_ROLE).clusterPermissions(allRestAdminPermissions())); + } + + public RolesRestApiIntegrationTest() { + super("roles", new TestDescriptor() { + @Override + public String entityJsonProperty() { + return "cluster_permissions"; + } + + @Override + public ToXContentObject entityPayload(Boolean hidden, Boolean reserved, Boolean _static) { + return roleWithClusterPermissions(hidden, reserved, _static, "a", "b"); + } + + @Override + public ToXContentObject jsonPropertyPayload() { + return randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray())); + } + + @Override + public Optional restAdminLimitedUser() { + return Optional.of(REST_API_ADMIN_ACTION_ROLES_ONLY); + } + }); + } + + @Override + void verifyCrudOperations(final Boolean hidden, final Boolean reserved, final TestRestClient client) throws Exception { + final var newRoleJson = Strings.toString( + XContentType.JSON, + role(hidden, reserved, randomClusterPermissions(false), randomIndexPermissions(false), randomTenantPermissions(false)) + ); + created(() -> client.putJson(apiPath("new_role"), newRoleJson)); + assertRole(ok(() -> client.get(apiPath("new_role"))), "new_role", hidden, reserved, newRoleJson); + + final var updatedRoleJson = Strings.toString( + XContentType.JSON, + role(hidden, reserved, randomClusterPermissions(false), randomIndexPermissions(false), randomTenantPermissions(false)) + ); + ok(() -> client.putJson(apiPath("new_role"), updatedRoleJson)); + assertRole(ok(() -> client.get(apiPath("new_role"))), "new_role", hidden, reserved, updatedRoleJson); + + ok(() -> client.delete(apiPath("new_role"))); + notFound(() -> client.get(apiPath("new_role"))); + + final var roleForPatch = role(hidden, reserved, configJsonArray("a", "b"), configJsonArray(), configJsonArray()); + ok(() -> client.patch(apiPath(), patch(addOp("new_role_for_patch", roleForPatch)))); + assertRole( + ok(() -> client.get(apiPath("new_role_for_patch"))), + "new_role_for_patch", + hidden, + reserved, + Strings.toString(XContentType.JSON, roleForPatch) + ); + + // TODO related to issue #4426 + ok(() -> client.patch(apiPath("new_role_for_patch"), patch(replaceOp("cluster_permissions", configJsonArray("a", "b"))))); + ok(() -> client.patch(apiPath("new_role_for_patch"), patch(addOp("index_permissions", randomIndexPermissions(false))))); + ok(() -> client.patch(apiPath("new_role_for_patch"), patch(addOp("tenant_permissions", randomTenantPermissions(false))))); + + ok(() -> client.patch(apiPath(), patch(removeOp("new_role_for_patch")))); + notFound(() -> client.get(apiPath("new_role_for_patch"))); + } + + @Override + void verifyBadRequestOperations(TestRestClient client) throws Exception { + // put + badRequest(() -> client.putJson(apiPath(randomAsciiAlphanumOfLength(5)), EMPTY_BODY)); + badRequest(() -> client.putJson(apiPath(randomAsciiAlphanumOfLength(5)), (builder, params) -> { + builder.startObject(); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + return builder.endObject(); + })); + assertInvalidKeys(badRequest(() -> client.putJson(apiPath(randomAsciiAlphanumOfLength(5)), (builder, params) -> { + builder.startObject(); + builder.field("unknown_json_property"); + configJsonArray("a", "b").toXContent(builder, params); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + return builder.endObject(); + })), "unknown_json_property"); + assertWrongDataType(badRequest(() -> client.putJson(apiPath(randomAsciiAlphanumOfLength(5)), (builder, params) -> { + builder.startObject(); + builder.field("cluster_permissions").value("a"); + builder.field("index_permissions").value("b"); + return builder.endObject(); + })), Map.of("cluster_permissions", "Array expected", "index_permissions", "Array expected")); + assertNullValuesInArray( + client.putJson( + apiPath(randomAsciiAlphanumOfLength(5)), + role(randomClusterPermissions(true), randomIndexPermissions(true), randomTenantPermissions(true)) + ) + ); + // patch + final var predefinedRoleName = randomAsciiAlphanumOfLength(4); + created(() -> client.putJson(apiPath(predefinedRoleName), role(configJsonArray("a", "b"), configJsonArray(), configJsonArray()))); + + badRequest(() -> client.patch(apiPath(), patch(addOp("some_new_role", EMPTY_BODY)))); + badRequest( + () -> client.patch( + apiPath(predefinedRoleName), + patch(replaceOp(randomFrom(List.of("cluster_permissions", "index_permissions", "tenant_permissions")), EMPTY_BODY)) + ) + ); + + badRequest( + () -> client.patch( + apiPath(randomAsciiAlphanumOfLength(5)), + patch(addOp(randomAsciiAlphanumOfLength(5), (ToXContentObject) (builder, params) -> { + builder.startObject(); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + return builder.endObject(); + })) + ) + ); + badRequest(() -> client.patch(apiPath(randomAsciiAlphanumOfLength(5)), (builder, params) -> { + builder.startObject(); + builder.field("unknown_json_property"); + configJsonArray("a", "b").toXContent(builder, params); + builder.field("cluster_permissions"); + randomClusterPermissions(false).toXContent(builder, params); + return builder.endObject(); + })); + assertWrongDataType( + badRequest(() -> client.patch(apiPath(), patch(addOp(randomAsciiAlphanumOfLength(5), (ToXContentObject) (builder, params) -> { + builder.startObject(); + builder.field("cluster_permissions").value("a"); + builder.field("index_permissions").value("b"); + return builder.endObject(); + })))), + Map.of("cluster_permissions", "Array expected", "index_permissions", "Array expected") + ); + assertWrongDataType( + badRequest(() -> client.patch(apiPath(predefinedRoleName), patch(replaceOp("cluster_permissions", "true")))), + Map.of("cluster_permissions", "Array expected") + ); + assertNullValuesInArray( + client.patch( + apiPath(), + patch( + addOp( + randomAsciiAlphanumOfLength(5), + role(randomClusterPermissions(true), randomIndexPermissions(true), randomTenantPermissions(true)) + ) + ) + ) + ); + // TODO related to issue #4426 + assertNullValuesInArray( + client.patch(apiPath(predefinedRoleName), patch(replaceOp("cluster_permissions", randomClusterPermissions(true)))) + ); + } + + @Override + void forbiddenToCreateEntityWithRestAdminPermissions(final TestRestClient client) throws Exception { + forbidden(() -> client.putJson(apiPath("new_rest_admin_role"), roleWithClusterPermissions(randomRestAdminPermission()))); + forbidden( + () -> client.patch( + apiPath(), + patch(addOp("new_rest_admin_action_group", roleWithClusterPermissions(randomRestAdminPermission()))) + ) + ); + } + + @Override + void forbiddenToUpdateAndDeleteExistingEntityWithRestAdminPermissions(final TestRestClient client) throws Exception { + // update + forbidden( + () -> client.putJson( + apiPath(REST_ADMIN_PERMISSION_ROLE), + role(randomClusterPermissions(false), randomIndexPermissions(false), randomTenantPermissions(false)) + ) + ); + forbidden( + () -> client.patch( + apiPath(), + patch( + replaceOp( + REST_ADMIN_PERMISSION_ROLE, + role(randomClusterPermissions(false), randomIndexPermissions(false), randomTenantPermissions(false)) + ) + ) + ) + ); + forbidden( + () -> client.patch( + apiPath(REST_ADMIN_PERMISSION_ROLE), + patch(replaceOp("cluster_permissions", randomClusterPermissions(false))) + ) + ); + // remove + forbidden(() -> client.patch(apiPath(), patch(removeOp(REST_ADMIN_PERMISSION_ROLE)))); + forbidden(() -> client.patch(apiPath(REST_ADMIN_PERMISSION_ROLE), patch(removeOp("cluster_permissions")))); + forbidden(() -> client.delete(apiPath(REST_ADMIN_PERMISSION_ROLE))); + } + + void assertRole( + final TestRestClient.HttpResponse response, + final String roleName, + final Boolean hidden, + final Boolean reserved, + final String expectedRoleJson + ) throws IOException { + final var expectedObjectNode = DefaultObjectMapper.readTree(expectedRoleJson); + final var actualObjectNode = response.bodyAsJsonNode().get(roleName); + final var expectedHidden = hidden != null && hidden; + final var expectedReserved = reserved != null && reserved; + assertThat(actualObjectNode.toPrettyString(), actualObjectNode.get("hidden").asBoolean(), is(expectedHidden)); + assertThat(actualObjectNode.toPrettyString(), actualObjectNode.get("reserved").asBoolean(), is(expectedReserved)); + assertThat(actualObjectNode.toPrettyString(), not(actualObjectNode.get("static").asBoolean())); + assertThat( + actualObjectNode.toPrettyString(), + actualObjectNode.get("cluster_permissions"), + is(expectedObjectNode.get("cluster_permissions")) + ); + // TODO related to issue #4426 + for (Iterator it = expectedObjectNode.get("index_permissions").elements(); it.hasNext();) { + final var indexPermission = (ObjectNode) it.next(); + if (indexPermission.has("dls") && indexPermission.get("dls").isNull()) { + indexPermission.remove("dls"); + } + if (indexPermission.has("fls") && indexPermission.get("fls").isNull()) { + indexPermission.set("fls", DefaultObjectMapper.objectMapper.createArrayNode()); + } + if (indexPermission.has("masked_fields") && indexPermission.get("masked_fields").isNull()) { + indexPermission.set("masked_fields", DefaultObjectMapper.objectMapper.createArrayNode()); + } + } + + assertThat( + actualObjectNode.toPrettyString(), + actualObjectNode.get("index_permissions"), + is(expectedObjectNode.get("index_permissions")) + ); + assertThat( + actualObjectNode.toPrettyString(), + actualObjectNode.get("tenant_permissions"), + is(expectedObjectNode.get("tenant_permissions")) + ); + } + + static ToXContentObject roleWithClusterPermissions(final String... clusterPermissions) { + return roleWithClusterPermissions(null, null, null, clusterPermissions); + } + + static ToXContentObject roleWithClusterPermissions( + final Boolean hidden, + final Boolean reserved, + final Boolean _static, + final String... clusterPermissions + ) { + return role( + hidden, + reserved, + _static, + (builder, params) -> configJsonArray(clusterPermissions).toXContent(builder, params), + null, + null + ); + } + + static ToXContentObject role( + final ToXContentObject clusterPermissions, + final ToXContentObject indexPermissions, + final ToXContentObject tenantPermissions + ) { + return role(null, null, null, clusterPermissions, indexPermissions, tenantPermissions); + } + + static ToXContentObject role( + final Boolean hidden, + final Boolean reserved, + final ToXContentObject clusterPermissions, + final ToXContentObject indexPermissions, + final ToXContentObject tenantPermissions + ) { + return role(hidden, reserved, null, clusterPermissions, indexPermissions, tenantPermissions); + } + + static ToXContentObject role( + final Boolean hidden, + final Boolean reserved, + final Boolean _static, + final ToXContentObject clusterPermissions, + final ToXContentObject indexPermissions, + final ToXContentObject tenantPermissions + ) { + return (builder, params) -> { + builder.startObject(); + builder.field("cluster_permissions"); + if (clusterPermissions != null) { + clusterPermissions.toXContent(builder, params); + } else { + builder.startArray().endArray(); + } + builder.field("index_permissions"); + if (indexPermissions != null) { + indexPermissions.toXContent(builder, params); + } else { + builder.startArray().endArray(); + } + builder.field("tenant_permissions"); + if (tenantPermissions != null) { + tenantPermissions.toXContent(builder, params); + } else { + builder.startArray().endArray(); + } + if (hidden != null) { + builder.field("hidden", hidden); + } + if (reserved != null) { + builder.field("reserved", reserved); + } + if (_static != null) { + builder.field("static", _static); + } + return builder.endObject(); + }; + } + + ToXContentObject randomClusterPermissions(final boolean useNulls) { + return useNulls + ? configJsonArray(generateArrayValues(useNulls)) + : randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray())); + } + + ToXContentObject randomIndexPermissions(final boolean useNulls) { + return (builder, params) -> { + final var possibleJson = useNulls + ? randomIndexPermission(useNulls) + : randomFrom(List.of(randomIndexPermission(false), (b, p) -> b)); + builder.startArray(); + possibleJson.toXContent(builder, params); + return builder.endArray(); + }; + } + + ToXContentObject randomIndexPermission(final boolean useNulls) { + return (builder, params) -> { + builder.startObject(); + + builder.field("index_patterns"); + randomIndexPatterns(useNulls).toXContent(builder, params); + + builder.field("dls"); + randomDls().toXContent(builder, params); + + builder.field("fls"); + randomFls(useNulls).toXContent(builder, params); + + builder.field("masked_fields"); + randomMaskedFields(useNulls).toXContent(builder, params); + + builder.field("allowed_actions"); + randomAllowedActions(useNulls).toXContent(builder, params); + + return builder.endObject(); + }; + } + + ToXContentObject randomIndexPatterns(final boolean useNulls) { + return useNulls + ? configJsonArray(generateArrayValues(useNulls)) + : randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray())); + } + + ToXContentObject randomTenantPermissions(final boolean useNulls) { + return (builder, params) -> { + final var possibleJson = useNulls ? tenantPermission(useNulls) : randomFrom(List.of(tenantPermission(false), (b, p) -> b)); + builder.startArray(); + possibleJson.toXContent(builder, params); + return builder.endArray(); + }; + } + + ToXContentObject tenantPermission(final boolean useNulls) { + return (builder, params) -> { + builder.startObject().field("tenant_patterns"); + randomFrom(List.of(configJsonArray(generateArrayValues(useNulls)), configJsonArray())).toXContent(builder, params); + builder.field("allowed_actions"); + randomAllowedActions(useNulls).toXContent(builder, params); + return builder.endObject(); + }; + } + + ToXContentObject randomDls() { + return randomFrom( + List.of((builder, params) -> builder.value(randomAsciiAlphanumOfLength(10)), (builder, params) -> builder.nullValue()) + ); + } + + ToXContentObject randomFls(final boolean useNullValues) { + return useNullValues + ? configJsonArray(generateArrayValues(useNullValues)) + : randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray(), (builder, params) -> builder.nullValue())); + } + + ToXContentObject randomMaskedFields(final boolean useNullValues) { + return useNullValues + ? configJsonArray(generateArrayValues(useNullValues)) + : randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray(), (builder, params) -> builder.nullValue())); + } + + ToXContentObject randomAllowedActions(final boolean useNullValues) { + return useNullValues + ? configJsonArray(generateArrayValues(useNullValues)) + : randomFrom(List.of(configJsonArray(generateArrayValues(false)), configJsonArray())); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java index 0c332c54d2..f29edeee24 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java +++ b/src/integrationTest/java/org/opensearch/test/framework/TestSecurityConfig.java @@ -146,13 +146,14 @@ public TestSecurityConfig user(User user) { } public TestSecurityConfig withRestAdminUser(final String name, final String... permissions) { - if (internalUsers.containsKey(name)) throw new RuntimeException("REST Admin " + name + " already exists"); - user(new User(name, "REST Admin with permissions: " + Arrays.toString(permissions)).reserved(true)); - final var roleName = name + "__rest_admin_role"; - roles(new Role(roleName).clusterPermissions(permissions)); + if (!internalUsers.containsKey(name)) { + user(new User(name, "REST Admin with permissions: " + Arrays.toString(permissions)).reserved(true)); + final var roleName = name + "__rest_admin_role"; + roles(new Role(roleName).clusterPermissions(permissions)); - rolesMapping.computeIfAbsent(roleName, RoleMapping::new).users(name); - rolesMapping.computeIfAbsent(REST_ADMIN_REST_API_ACCESS, RoleMapping::new).users(name); + rolesMapping.computeIfAbsent(roleName, RoleMapping::new).users(name); + rolesMapping.computeIfAbsent(REST_ADMIN_REST_API_ACCESS, RoleMapping::new).users(name); + } return this; } diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java index 3af971e208..4c29d6e934 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/RolesApiAction.java @@ -167,7 +167,10 @@ public Settings settings() { @Override public Map allowedKeys() { final ImmutableMap.Builder allowedKeys = ImmutableMap.builder(); - if (isCurrentUserAdmin()) allowedKeys.put("reserved", DataType.BOOLEAN); + if (isCurrentUserAdmin()) { + allowedKeys.put("hidden", DataType.BOOLEAN); + allowedKeys.put("reserved", DataType.BOOLEAN); + } return allowedKeys.put("cluster_permissions", DataType.ARRAY) .put("tenant_permissions", DataType.ARRAY) .put("index_permissions", DataType.ARRAY) diff --git a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java index d60e723d89..2e900169db 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java +++ b/src/main/java/org/opensearch/security/dlic/rest/support/Utils.java @@ -17,7 +17,6 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -38,9 +37,9 @@ import org.opensearch.common.xcontent.XContentHelper; import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; +import org.opensearch.core.common.Strings; import org.opensearch.core.common.bytes.BytesReference; import org.opensearch.core.common.transport.TransportAddress; -import org.opensearch.core.xcontent.MediaTypeRegistry; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.core.xcontent.ToXContent; import org.opensearch.core.xcontent.XContentParser; @@ -125,17 +124,13 @@ public static Object toConfigObject(final JsonNode content, final Class clazz public static JsonNode convertJsonToJackson(ToXContent jsonContent, boolean omitDefaults) { try { - Map pm = new HashMap<>(1); - pm.put("omit_defaults", String.valueOf(omitDefaults)); - ToXContent.MapParams params = new ToXContent.MapParams(pm); - - final BytesReference bytes = org.opensearch.core.xcontent.XContentHelper.toXContent( - jsonContent, - MediaTypeRegistry.JSON, - params, - false + return DefaultObjectMapper.readTree( + Strings.toString( + XContentType.JSON, + jsonContent, + new ToXContent.MapParams(Map.of("omit_defaults", String.valueOf(omitDefaults))) + ) ); - return DefaultObjectMapper.readTree(bytes.utf8ToString()); } catch (IOException e1) { throw ExceptionsHelper.convertToOpenSearchException(e1); } diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java deleted file mode 100644 index eb3ad7d4e5..0000000000 --- a/src/test/java/org/opensearch/security/dlic/rest/api/RolesApiTest.java +++ /dev/null @@ -1,883 +0,0 @@ -/* - * 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.dlic.rest.api; - -import java.util.List; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.hc.core5.http.Header; -import org.apache.http.HttpStatus; -import org.junit.Assert; -import org.junit.Test; - -import org.opensearch.common.settings.Settings; -import org.opensearch.common.xcontent.XContentType; -import org.opensearch.security.DefaultObjectMapper; -import org.opensearch.security.dlic.rest.validation.RequestContentValidator; -import org.opensearch.security.support.SecurityJsonNode; -import org.opensearch.security.test.helper.file.FileHelper; -import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; - -import static org.opensearch.security.OpenSearchSecurityPlugin.PLUGINS_PREFIX; -import static org.opensearch.security.support.ConfigConstants.SECURITY_RESTAPI_ADMIN_ENABLED; - -public class RolesApiTest extends AbstractRestApiUnitTest { - private final String ENDPOINT; - - protected String getEndpointPrefix() { - return PLUGINS_PREFIX; - } - - public RolesApiTest() { - ENDPOINT = getEndpointPrefix() + "/api"; - } - - @Test - public void testPutRole() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - // check roles exists - HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/admin", FileHelper.loadFile("restapi/simple_role.json")); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - - response = rh.executePutRequest(ENDPOINT + "/roles/lala", "{ \"cluster_permissions\": [\"*\"] }"); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - - response = rh.executePutRequest(ENDPOINT + "/roles/empty", "{ \"cluster_permissions\": [] }"); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - } - - @Test - public void testAllRolesForSuperAdmin() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - - checkSuperAdminRoles(new Header[0]); - } - - @Test - public void testAllRolesForRestAdmin() throws Exception { - setupWithRestRoles(Settings.builder().put(SECURITY_RESTAPI_ADMIN_ENABLED, true).build()); - final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user"); - rh.sendAdminCertificate = false; - checkSuperAdminRoles(new Header[] { restApiAdminHeader }); - } - - @Test - public void testAllRolesForRolesRestAdmin() throws Exception { - setupWithRestRoles(Settings.builder().put(SECURITY_RESTAPI_ADMIN_ENABLED, true).build()); - final Header restApiAdminRolesHeader = encodeBasicHeader("rest_api_admin_roles", "rest_api_admin_roles"); - rh.sendAdminCertificate = false; - checkSuperAdminRoles(new Header[] { restApiAdminRolesHeader }); - } - - void checkSuperAdminRoles(final Header[] header) { - HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertFalse(response.getBody().contains("_meta")); - - // Super admin should be able to see all roles including hidden - Assert.assertTrue(response.getBody().contains("opendistro_security_hidden")); - } - - @Test - public void testPutDuplicateKeys() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - HttpResponse response = rh.executePutRequest( - ENDPOINT + "/roles/dup", - "{ \"cluster_permissions\": [\"*\"], \"cluster_permissions\": [\"*\"] }" - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - assertHealthy(); - } - - @Test - public void testPutUnknownKey() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - HttpResponse response = rh.executePutRequest( - ENDPOINT + "/roles/dup", - "{ \"unknownkey\": [\"*\"], \"cluster_permissions\": [\"*\"] }" - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("invalid_keys")); - assertHealthy(); - } - - @Test - public void testPutInvalidJson() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - HttpResponse response = rh.executePutRequest( - ENDPOINT + "/roles/dup", - "{ \"invalid\"::{{ [\"*\"], \"cluster_permissions\": [\"*\"] }" - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - assertHealthy(); - } - - @Test - public void testRolesApi() throws Exception { - - setup(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - - // create index - setupStarfleetIndex(); - - // add user picard, role starfleet, maps to opendistro_security_role_starfleet - addUserWithPassword("picard", "picardpicardpicardpicard", new String[] { "starfleet", "captains" }, HttpStatus.SC_CREATED); - checkReadAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - checkWriteAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - rh.sendAdminCertificate = true; - verifyGetForSuperAdmin(new Header[0]); - rh.sendAdminCertificate = true; - verifyDeleteForSuperAdmin(new Header[0], true); - rh.sendAdminCertificate = true; - verifyPutForSuperAdmin(new Header[0], true); - rh.sendAdminCertificate = true; - verifyPatchForSuperAdmin(new Header[0], true); - } - - void verifyGetForSuperAdmin(final Header[] header) throws Exception { - // check roles exists - HttpResponse response = rh.executeGetRequest(ENDPOINT + "/roles", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - - // -- GET - // GET opendistro_security_role_starfleet - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - JsonNode settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(1, settings.size()); - - // GET, role does not exist - response = rh.executeGetRequest(ENDPOINT + "/roles/nothinghthere", header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - response = rh.executeGetRequest(ENDPOINT + "/roles/", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - - response = rh.executeGetRequest(ENDPOINT + "/roles", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("\"cluster_permissions\":[\"*\"]")); - Assert.assertFalse(response.getBody().contains("\"cluster_permissions\" : [")); - - response = rh.executeGetRequest(ENDPOINT + "/roles?pretty", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertFalse(response.getBody().contains("\"cluster_permissions\":[\"*\"]")); - Assert.assertTrue(response.getBody().contains("\"cluster_permissions\" : [")); - - // Super admin should be able to describe hidden role - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_hidden", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("\"hidden\":true")); - } - - void verifyDeleteForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception { - // -- DELETE - // Non-existing role - HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/roles/idonotexist", header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // read only role, SuperAdmin can delete the read-only role - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - - // hidden role allowed for superadmin - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("'opendistro_security_internal' deleted.")); - - // remove complete role mapping for opendistro_security_role_starfleet_captains - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - rh.sendAdminCertificate = false; - // user has only role starfleet left, role has READ access only - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picardpicardpicardpicard", "sf", "_doc", 1); - // ES7 only supports one doc type, but OpenSearch permission checks run first - // So we also get a 403 FORBIDDEN when tring to add new document type - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - rh.sendAdminCertificate = sendAdminCert; - // remove also starfleet role, nothing is allowed anymore - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - checkReadAccess(HttpStatus.SC_FORBIDDEN, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - checkWriteAccess(HttpStatus.SC_FORBIDDEN, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - } - - void verifyPutForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception { - // -- PUT - // put with empty roles, must fail - HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "", header); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - JsonNode settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(RequestContentValidator.ValidationError.PAYLOAD_MANDATORY.message(), settings.get("reason").asText()); - - // put new configuration with invalid payload, must fail - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - FileHelper.loadFile("restapi/roles_not_parseable.json"), - header - ); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.BODY_NOT_PARSEABLE.message(), settings.get("reason").asText()); - - // put new configuration with invalid keys, must fail - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - FileHelper.loadFile("restapi/roles_invalid_keys.json"), - header - ); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.INVALID_CONFIGURATION.message(), settings.get("reason").asText()); - Assert.assertTrue(settings.get(RequestContentValidator.INVALID_KEYS_KEY).get("keys").asText().contains("indexx_permissions")); - Assert.assertTrue(settings.get(RequestContentValidator.INVALID_KEYS_KEY).get("keys").asText().contains("kluster_permissions")); - - // put new configuration with wrong datatypes, must fail - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - FileHelper.loadFile("restapi/roles_wrong_datatype.json"), - header - ); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.WRONG_DATATYPE.message(), settings.get("reason").asText()); - Assert.assertTrue(settings.get("cluster_permissions").asText().equals("Array expected")); - - // put read only role, must be forbidden - // But SuperAdmin can still create it - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_transport_client", - FileHelper.loadFile("restapi/roles_captains.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - - // put hidden role, must be forbidden, but allowed for super admin - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_internal", - FileHelper.loadFile("restapi/roles_captains.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - - // restore starfleet role - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - FileHelper.loadFile("restapi/roles_starfleet.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - rh.sendAdminCertificate = false; - checkReadAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - // now picard is only in opendistro_security_role_starfleet, which has write access to - // all indices. We collapse all document types in ODFE7 so this permission in the - // starfleet role grants all permissions: - // _doc: - // - 'indices:*' - checkWriteAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - rh.sendAdminCertificate = sendAdminCert; - - // restore captains role - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - rh.sendAdminCertificate = false; - checkReadAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - checkWriteAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_complete_invalid.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_multiple_2.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - // check tenants - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_tenants.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(2, settings.size()); - Assert.assertEquals(settings.get("status").asText(), "OK"); - - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(1, settings.size()); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(1) - .get("tenant_patterns") - .get(0) - .asString(), - "tenant1" - ); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(1) - .get("allowed_actions") - .get(0) - .asString(), - "kibana_all_read" - ); - - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(0) - .get("tenant_patterns") - .get(0) - .asString(), - "tenant2" - ); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(0) - .get("allowed_actions") - .get(0) - .asString(), - "kibana_all_write" - ); - - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_tenants2.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(2, settings.size()); - Assert.assertEquals(settings.get("status").asText(), "OK"); - - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(1, settings.size()); - - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(0) - .get("tenant_patterns") - .get(0) - .asString(), - "tenant2" - ); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(0) - .get("tenant_patterns") - .get(1) - .asString(), - "tenant4" - ); - - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(0) - .get("allowed_actions") - .get(0) - .asString(), - "kibana_all_write" - ); - - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(1) - .get("tenant_patterns") - .get(0) - .asString(), - "tenant1" - ); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(1) - .get("tenant_patterns") - .get(1) - .asString(), - "tenant3" - ); - Assert.assertEquals( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions") - .get(1) - .get("allowed_actions") - .get(0) - .asString(), - "kibana_all_read" - ); - - // remove tenants from role - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_no_tenants.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(2, settings.size()); - Assert.assertEquals(settings.get("status").asText(), "OK"); - - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(1, settings.size()); - Assert.assertFalse( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.cluster_permissions").get(0).isNull() - ); - Assert.assertTrue( - new SecurityJsonNode(settings).getDotted("opendistro_security_role_starfleet_captains.tenant_permissions").get(0).isNull() - ); - - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet_captains", - FileHelper.loadFile("restapi/roles_captains_tenants_malformed.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - Assert.assertEquals(settings.get("status").asText(), "error"); - Assert.assertEquals(settings.get("reason").asText(), RequestContentValidator.ValidationError.INVALID_CONFIGURATION.message()); - } - - void verifyPatchForSuperAdmin(final Header[] header, final boolean sendAdminCert) throws Exception { - // -- PATCH - // PATCH on non-existing resource - rh.sendAdminCertificate = sendAdminCert; - HttpResponse response = rh.executePatchRequest( - ENDPOINT + "/roles/imnothere", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header - ); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // PATCH read only resource, must be forbidden - // SuperAdmin can patch it - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_transport_client", - "[{ \"op\": \"add\", \"path\": \"/description\", \"value\": \"foo\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - - // PATCH hidden resource, must be not found, can be found for superadmin, but will fail with no path present exception - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_internal", - "[{ \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_role_starfleet", - "[{ \"op\": \"add\", \"path\": \"/hidden\", \"value\": true }]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertTrue( - response.getBody(), - response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*") - ); - - List permissions = null; - - // PATCH - /* - * how to patch with new v7 config format? - * rh.sendAdminCertificate = true; - response = rh.executePatchRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", "[{ \"op\": \"add\", \"path\": \"/index_permissions/sf/_doc/-\", \"value\": \"SEARCH\" }]", new Header[0]); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", new Header[0]); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - settings = DefaultObjectMapper.readTree(response.getBody()); - permissions = DefaultObjectMapper.objectMapper.convertValue(settings.get("opendistro_security_role_starfleet").get("indices").get("sf").get("_doc"), List.class); - Assert.assertNotNull(permissions); - Assert.assertEquals(2, permissions.size()); - Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_READ")); - Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_SEARCH")); */ - - // -- PATCH on whole config resource - // PATCH on non-existing resource - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/imnothere/a/b/c\", \"value\": [ \"foo\", \"bar\" ] }]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - // PATCH read only resource, must be forbidden - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/a\", \"value\": [ \"foo\", \"bar\" ] }]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - // PATCH hidden resource, must be bad request - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/a\", \"value\": [ \"foo\", \"bar\" ] }]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - - // PATCH delete read only resource, must be forbidden - // SuperAdmin can delete read only user - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_transport_client\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - - // PATCH hidden resource, must be bad request, but allowed for superadmin - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"remove\", \"path\": \"/opendistro_security_internal\"}]", - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - Assert.assertTrue(response.getBody().contains("\"message\":\"Resource updated.")); - - // PATCH value of hidden flag, must fail with validation error - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/newnewnew\", \"value\": { \"hidden\": true, \"index_permissions\" : " - + "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertTrue(response.getBody().matches(".*\"invalid_keys\"\\s*:\\s*\\{\\s*\"keys\"\\s*:\\s*\"hidden\"\\s*\\}.*")); - - // PATCH - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest( - ENDPOINT + "/roles", - "[{ \"op\": \"add\", \"path\": \"/bulknew1\", \"value\": { \"index_permissions\" : " - + "[ {\"index_patterns\" : [ \"sf\" ],\"allowed_actions\" : [ \"OPENDISTRO_SECURITY_READ\" ]}] }}]", - header - ); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - JsonNode settings = DefaultObjectMapper.readTree(response.getBody()); - permissions = new SecurityJsonNode(settings).get("bulknew1").get("index_permissions").get(0).get("allowed_actions").asList(); - Assert.assertNotNull(permissions); - Assert.assertEquals(1, permissions.size()); - Assert.assertTrue(permissions.contains("OPENDISTRO_SECURITY_READ")); - - // delete resource - rh.sendAdminCertificate = sendAdminCert; - response = rh.executePatchRequest(ENDPOINT + "/roles", "[{ \"op\": \"remove\", \"path\": \"/bulknew1\"}]", header); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - response = rh.executeGetRequest(ENDPOINT + "/roles/bulknew1", header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // put valid field masks - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_field_mask_valid", - FileHelper.loadFile("restapi/roles_field_masks_valid.json"), - header - ); - Assert.assertEquals(response.getBody(), HttpStatus.SC_CREATED, response.getStatusCode()); - - // put invalid field masks - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_field_mask_invalid", - FileHelper.loadFile("restapi/roles_field_masks_invalid.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - } - - @Test - public void testRolesApiWithAllRestApiPermissions() throws Exception { - setupWithRestRoles(Settings.builder().put(SECURITY_RESTAPI_ADMIN_ENABLED, true).build()); - - final Header restApiAdminHeader = encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user"); - - rh.sendAdminCertificate = false; - setupStarfleetIndex(); - - // add user picard, role starfleet, maps to opendistro_security_role_starfleet - addUserWithPassword("picard", "picardpicardpicardpicard", new String[] { "starfleet", "captains" }, HttpStatus.SC_CREATED); - checkReadAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - checkWriteAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - verifyGetForSuperAdmin(new Header[] { restApiAdminHeader }); - verifyDeleteForSuperAdmin(new Header[] { restApiAdminHeader }, false); - verifyPutForSuperAdmin(new Header[] { restApiAdminHeader }, false); - verifyPatchForSuperAdmin(new Header[] { restApiAdminHeader }, false); - } - - @Test - public void testRolesApiWithRestApiRolePermission() throws Exception { - setupWithRestRoles(Settings.builder().put(SECURITY_RESTAPI_ADMIN_ENABLED, true).build()); - - final Header restApiRolesHeader = encodeBasicHeader("rest_api_admin_roles", "rest_api_admin_roles"); - - rh.sendAdminCertificate = false; - setupStarfleetIndex(); - - // add user picard, role starfleet, maps to opendistro_security_role_starfleet - addUserWithPassword("picard", "picardpicardpicardpicard", new String[] { "starfleet", "captains" }, HttpStatus.SC_CREATED); - checkReadAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - checkWriteAccess(HttpStatus.SC_OK, "picard", "picardpicardpicardpicard", "sf", "_doc", 0); - - verifyGetForSuperAdmin(new Header[] { restApiRolesHeader }); - verifyDeleteForSuperAdmin(new Header[] { restApiRolesHeader }, false); - verifyPutForSuperAdmin(new Header[] { restApiRolesHeader }, false); - verifyPatchForSuperAdmin(new Header[] { restApiRolesHeader }, false); - } - - @Test - public void testCrudRestApiAdminRoleForbidden() throws Exception { - setupWithRestRoles(Settings.builder().put(SECURITY_RESTAPI_ADMIN_ENABLED, true).build()); - rh.sendAdminCertificate = false; - - final var userHeaders = List.of( - encodeBasicHeader("admin", "admin"), - encodeBasicHeader("test", "test"), - encodeBasicHeader("rest_api_admin_user", "rest_api_admin_user"), - encodeBasicHeader("rest_api_admin_roles", "rest_api_admin_roles") - ); - for (final var userHeader : userHeaders) { - final String restAdminPermissionsPayload = createRestAdminPermissionsPayload("cluster/*"); - // attempt to create a new role - verifyPutForbidden("new_rest_admin_role", restAdminPermissionsPayload, userHeader); - verifyPatchForbidden(createPatchRestAdminPermissionsPayload("new_rest_admin_role", "add"), userHeader); - - // attempt to update existing rest admin role - verifyPutForbidden("rest_api_admin_full_access", restAdminPermissionsPayload, userHeader); - verifyPatchForbidden(createPatchRestAdminPermissionsPayload("rest_api_admin_full_access", "replace"), userHeader); - - // attempt to update non rest admin role with REST admin permissions - verifyPutForbidden("opendistro_security_role_starfleet_captains", restAdminPermissionsPayload, userHeader); - verifyPatchForbidden( - createPatchRestAdminPermissionsPayload("opendistro_security_role_starfleet_captains", "replace"), - userHeader - ); - - // attempt to remove REST admin role - verifyDeleteForbidden("rest_api_admin_full_access", userHeader); - verifyPatchForbidden(createPatchRestAdminPermissionsPayload("rest_api_admin_full_access", "remove"), userHeader); - } - } - - void verifyPutForbidden(final String roleName, final String restAdminPermissionsPayload, final Header... header) { - HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/" + roleName, restAdminPermissionsPayload, header); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - } - - void verifyPatchForbidden(final String restAdminPermissionsPayload, final Header... header) { - HttpResponse response = rh.executePatchRequest(ENDPOINT + "/roles", restAdminPermissionsPayload, header); - Assert.assertEquals(response.getBody(), HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - } - - void verifyDeleteForbidden(final String roleName, final Header... header) { - HttpResponse response = rh.executeDeleteRequest(ENDPOINT + "/roles/" + roleName, header); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - } - - private String createPatchRestAdminPermissionsPayload(final String roleName, final String op) throws JsonProcessingException { - final ArrayNode rootNode = DefaultObjectMapper.objectMapper.createArrayNode(); - final ObjectNode opAddObjectNode = DefaultObjectMapper.objectMapper.createObjectNode(); - final ObjectNode clusterPermissionsNode = DefaultObjectMapper.objectMapper.createObjectNode(); - clusterPermissionsNode.set("cluster_permissions", clusterPermissionsForRestAdmin("cluster/*")); - if ("add".equals(op)) { - opAddObjectNode.put("op", "add").put("path", "/" + roleName).set("value", clusterPermissionsNode); - rootNode.add(opAddObjectNode); - } - - if ("remove".equals(op)) { - final ObjectNode opRemoveObjectNode = DefaultObjectMapper.objectMapper.createObjectNode(); - opRemoveObjectNode.put("op", "remove").put("path", "/" + roleName); - rootNode.add(opRemoveObjectNode); - } - - if ("replace".equals(op)) { - final ObjectNode replaceRemoveObjectNode = DefaultObjectMapper.objectMapper.createObjectNode(); - replaceRemoveObjectNode.put("op", "replace") - .put("path", "/" + roleName + "/cluster_permissions") - .set("value", clusterPermissionsForRestAdmin("*")); - - rootNode.add(replaceRemoveObjectNode); - } - return DefaultObjectMapper.objectMapper.writeValueAsString(rootNode); - } - - @Test - public void testRolesApiForNonSuperAdmin() throws Exception { - setupWithRestRoles(); - - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = false; - rh.sendHTTPClientCredentials = true; - checkNonSuperAdminRoles(new Header[0]); - } - - void checkNonSuperAdminRoles(final Header[] header) throws Exception { - HttpResponse response; - - // Delete read only roles - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_transport_client", header); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - - // Put read only roles - response = rh.executePutRequest( - ENDPOINT + "/roles/opendistro_security_transport_client", - FileHelper.loadFile("restapi/roles_captains.json"), - header - ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - - // Patch single read only roles - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_transport_client", - "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - - // Patch multiple read only roles - response = rh.executePatchRequest( - ENDPOINT + "/roles/", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_transport_client/description\", \"value\": \"foo\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_FORBIDDEN, response.getStatusCode()); - - // get hidden role - response = rh.executeGetRequest(ENDPOINT + "/roles/opendistro_security_internal", header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // delete hidden role - response = rh.executeDeleteRequest(ENDPOINT + "/roles/opendistro_security_internal", header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // put hidden role - String body = FileHelper.loadFile("restapi/roles_captains.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_internal", body, header); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // Patch single hidden roles - response = rh.executePatchRequest( - ENDPOINT + "/roles/opendistro_security_internal", - "[{ \"op\": \"replace\", \"path\": \"/description\", \"value\": \"foo\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - - // Patch multiple hidden roles - response = rh.executePatchRequest( - ENDPOINT + "/roles/", - "[{ \"op\": \"add\", \"path\": \"/opendistro_security_internal/description\", \"value\": \"foo\" }]", - header - ); - Assert.assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - } - - @Test - public void checkNullElementsInArray() throws Exception { - setup(); - rh.keystore = "restapi/kirk-keystore.jks"; - rh.sendAdminCertificate = true; - - String body = FileHelper.loadFile("restapi/roles_null_array_element_cluster_permissions.json"); - HttpResponse response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - Settings settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - - body = FileHelper.loadFile("restapi/roles_null_array_element_index_permissions.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - - body = FileHelper.loadFile("restapi/roles_null_array_element_tenant_permissions.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - - body = FileHelper.loadFile("restapi/roles_null_array_element_index_patterns.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - - body = FileHelper.loadFile("restapi/roles_null_array_element_masked_fields.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - - body = FileHelper.loadFile("restapi/roles_null_array_element_allowed_actions.json"); - response = rh.executePutRequest(ENDPOINT + "/roles/opendistro_security_role_starfleet", body, new Header[0]); - settings = Settings.builder().loadFromSource(response.getBody(), XContentType.JSON).build(); - Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, response.getStatusCode()); - Assert.assertEquals(RequestContentValidator.ValidationError.NULL_ARRAY_ELEMENT.message(), settings.get("reason")); - } - -} diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRolesApiTests.java b/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRolesApiTests.java deleted file mode 100644 index 118f8e1ebe..0000000000 --- a/src/test/java/org/opensearch/security/dlic/rest/api/legacy/LegacyRolesApiTests.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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.dlic.rest.api.legacy; - -import org.opensearch.security.dlic.rest.api.RolesApiTest; - -import static org.opensearch.security.OpenSearchSecurityPlugin.LEGACY_OPENDISTRO_PREFIX; - -public class LegacyRolesApiTests extends RolesApiTest { - @Override - protected String getEndpointPrefix() { - return LEGACY_OPENDISTRO_PREFIX; - } -}