diff --git a/src/integrationTest/java/org/opensearch/security/rest/AuthZinRestLayerTests.java b/src/integrationTest/java/org/opensearch/security/rest/AuthZinRestLayerTests.java new file mode 100644 index 0000000000..96aea9d4bc --- /dev/null +++ b/src/integrationTest/java/org/opensearch/security/rest/AuthZinRestLayerTests.java @@ -0,0 +1,243 @@ +/* + * 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.rest; + +import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; +import org.apache.hc.core5.http.HttpStatus; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opensearch.test.framework.AuditCompliance; +import org.opensearch.test.framework.AuditConfiguration; +import org.opensearch.test.framework.AuditFilters; +import org.opensearch.test.framework.TestSecurityConfig; +import org.opensearch.test.framework.TestSecurityConfig.Role; +import org.opensearch.test.framework.audit.AuditLogsRule; +import org.opensearch.test.framework.cluster.ClusterManager; +import org.opensearch.test.framework.cluster.LocalCluster; +import org.opensearch.test.framework.cluster.TestRestClient; +import org.opensearch.test.framework.testplugins.dummy.CustomLegacyTestPlugin; +import org.opensearch.test.framework.testplugins.dummyprotected.CustomRestProtectedTestPlugin; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.POST; +import static org.opensearch.security.auditlog.impl.AuditCategory.FAILED_LOGIN; +import static org.opensearch.security.auditlog.impl.AuditCategory.GRANTED_PRIVILEGES; +import static org.opensearch.security.auditlog.impl.AuditCategory.MISSING_PRIVILEGES; +import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.privilegePredicateRESTLayer; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.privilegePredicateTransportLayer; + +@RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) +@ThreadLeakScope(ThreadLeakScope.Scope.NONE) +public class AuthZinRestLayerTests { + protected final static TestSecurityConfig.User REST_ONLY = new TestSecurityConfig.User("rest_only").roles( + new Role("rest_only_role").clusterPermissions("security:dummy_protected/get").clusterPermissions("cluster:admin/dummy_plugin/dummy") + ); + + protected final static TestSecurityConfig.User TRANSPORT_ONLY = new TestSecurityConfig.User("transport_only").roles( + new Role("transport_only_role").clusterPermissions("cluster:admin/dummy_plugin/dummy") + ); + + protected final static TestSecurityConfig.User REST_PLUS_TRANSPORT = new TestSecurityConfig.User("rest_plus_transport").roles( + new Role("rest_plus_transport_role").clusterPermissions("security:dummy_protected/get") + .clusterPermissions("cluster:admin/dummy_plugin/dummy", "cluster:admin/dummy_protected_plugin/dummy/get") + ); + + protected final static TestSecurityConfig.User NO_PERM = new TestSecurityConfig.User("no_perm").roles(new Role("no_perm_role")); + + protected final static TestSecurityConfig.User UNREGISTERED = new TestSecurityConfig.User("unregistered"); + + public static final String UNPROTECTED_BASE_ENDPOINT = "_plugins/_dummy"; + public static final String PROTECTED_BASE_ENDPOINT = "_plugins/_dummy_protected"; + public static final String UNPROTECTED_API = UNPROTECTED_BASE_ENDPOINT + "/dummy"; + public static final String PROTECTED_API = PROTECTED_BASE_ENDPOINT + "/dummy"; + + @ClassRule + public static LocalCluster cluster = new LocalCluster.Builder().clusterManager(ClusterManager.THREE_CLUSTER_MANAGERS) + .authc(AUTHC_HTTPBASIC_INTERNAL) + .users(REST_ONLY, REST_PLUS_TRANSPORT, TRANSPORT_ONLY, NO_PERM) + .plugin(CustomLegacyTestPlugin.class) + .plugin(CustomRestProtectedTestPlugin.class) + .audit( + new AuditConfiguration(true).compliance(new AuditCompliance().enabled(true)) + .filters(new AuditFilters().enabledRest(true).enabledTransport(true).resolveBulkRequests(true)) + ) + .build(); + + @Rule + public AuditLogsRule auditLogsRule = new AuditLogsRule(); + + /** Basic Access check */ + + @Test + public void testShouldNotAllowUnregisteredUsers() { + try (TestRestClient client = cluster.getRestClient(UNREGISTERED)) { + // Legacy plugin + assertThat(client.get(UNPROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(FAILED_LOGIN, UNREGISTERED, GET, "/" + UNPROTECTED_API)); + + // Protected Routes plugin + assertThat(client.get(PROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(FAILED_LOGIN, UNREGISTERED, GET, "/" + PROTECTED_API)); + } + } + + @Test + public void testAccessDeniedForUserWithNoPermissions() { + try (TestRestClient client = cluster.getRestClient(NO_PERM)) { + // fail at Transport (won't have a rest authz success audit log since this is not a protected endpoint) + assertThat(client.get(UNPROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer(MISSING_PRIVILEGES, NO_PERM, "DummyRequest", "cluster:admin/dummy_plugin/dummy") + ); + + // fail at REST + assertThat(client.get(PROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(MISSING_PRIVILEGES, NO_PERM, GET, "/" + PROTECTED_API)); + } + } + + /** AuthZ in REST Layer check */ + + @Test + public void testShouldAllowAtRestAndBlockAtTransport() { + try (TestRestClient client = cluster.getRestClient(REST_ONLY)) { + assertThat(client.get(PROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + // granted at Rest layer + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(GRANTED_PRIVILEGES, REST_ONLY, GET, "/" + PROTECTED_API)); + // missing at Transport layer + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer( + MISSING_PRIVILEGES, + REST_ONLY, + "DummyRequest", + "cluster:admin/dummy_protected_plugin/dummy/get" + ) + ); + } + } + + @Test + public void testShouldAllowAtRestAndTransport() { + try (TestRestClient client = cluster.getRestClient(REST_PLUS_TRANSPORT)) { + assertOKResponseFromProtectedPlugin(client); + + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(GRANTED_PRIVILEGES, REST_PLUS_TRANSPORT, GET, "/" + PROTECTED_API)); + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer( + GRANTED_PRIVILEGES, + REST_PLUS_TRANSPORT, + "DummyRequest", + "cluster:admin/dummy_protected_plugin/dummy/get" + ) + ); + } + } + + @Test + public void testShouldBlockAccessToEndpointForWhichUserHasNoPermission() { + try (TestRestClient client = cluster.getRestClient(REST_ONLY)) { + assertThat(client.post(PROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(MISSING_PRIVILEGES, REST_ONLY, POST, "/" + PROTECTED_API)); + } + + try (TestRestClient client = cluster.getRestClient(REST_PLUS_TRANSPORT)) { + assertThat(client.post(PROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(MISSING_PRIVILEGES, REST_PLUS_TRANSPORT, POST, "/" + PROTECTED_API)); + } + } + + /** Backwards compatibility check */ + + @Test + public void testBackwardsCompatibility() { + + // TRANSPORT_ONLY should have access to legacy endpoint, but not protected endpoint + try (TestRestClient client = cluster.getRestClient(TRANSPORT_ONLY)) { + TestRestClient.HttpResponse res = client.get(PROTECTED_API); + assertThat(res.getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(MISSING_PRIVILEGES, TRANSPORT_ONLY, GET, "/" + PROTECTED_API)); + + assertOKResponseFromLegacyPlugin(client); + // check that there is no log for REST layer AuthZ since this is an unprotected endpoint + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(GRANTED_PRIVILEGES, TRANSPORT_ONLY, GET, UNPROTECTED_API)); + // check that there is exactly 1 message for Transport Layer privilege evaluation + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer(GRANTED_PRIVILEGES, TRANSPORT_ONLY, "DummyRequest", "cluster:admin/dummy_plugin/dummy") + ); + } + + // REST_ONLY should have access to legacy endpoint (protected endpoint already tested above) + try (TestRestClient client = cluster.getRestClient(REST_ONLY)) { + assertOKResponseFromLegacyPlugin(client); + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(GRANTED_PRIVILEGES, REST_ONLY, GET, UNPROTECTED_API)); + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer(GRANTED_PRIVILEGES, REST_ONLY, "DummyRequest", "cluster:admin/dummy_plugin/dummy") + ); + } + + // DUMMY_WITH_TRANSPORT_PERM should have access to legacy endpoint (protected endpoint already tested above) + try (TestRestClient client = cluster.getRestClient(REST_PLUS_TRANSPORT)) { + assertOKResponseFromLegacyPlugin(client); + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(GRANTED_PRIVILEGES, REST_PLUS_TRANSPORT, GET, UNPROTECTED_API)); + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer( + GRANTED_PRIVILEGES, + REST_PLUS_TRANSPORT, + "DummyRequest", + "cluster:admin/dummy_plugin/dummy" + ) + ); + } + + // NO_PERM should not have access to legacy endpoint (protected endpoint already tested above) + try (TestRestClient client = cluster.getRestClient(NO_PERM)) { + assertThat(client.get(UNPROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_FORBIDDEN)); + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(MISSING_PRIVILEGES, NO_PERM, GET, UNPROTECTED_API)); + auditLogsRule.assertExactlyOne( + privilegePredicateTransportLayer(MISSING_PRIVILEGES, NO_PERM, "DummyRequest", "cluster:admin/dummy_plugin/dummy") + ); + } + + // UNREGISTERED should not have access to legacy endpoint (protected endpoint already tested above) + try (TestRestClient client = cluster.getRestClient(UNREGISTERED)) { + assertThat(client.get(UNPROTECTED_API).getStatusCode(), equalTo(HttpStatus.SC_UNAUTHORIZED)); + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(MISSING_PRIVILEGES, UNREGISTERED, GET, UNPROTECTED_API)); + auditLogsRule.assertExactly( + 0, + privilegePredicateTransportLayer(MISSING_PRIVILEGES, UNREGISTERED, "DummyRequest", "cluster:admin/dummy_plugin/dummy") + ); + auditLogsRule.assertExactly(0, privilegePredicateRESTLayer(FAILED_LOGIN, UNREGISTERED, GET, UNPROTECTED_API)); + } + } + + /** Helper Methods */ + private void assertOKResponseFromLegacyPlugin(TestRestClient client) { + String expectedResponseFromLegacyPlugin = "{\"response_string\":\"Hello from dummy plugin\"}"; + TestRestClient.HttpResponse res = client.get(UNPROTECTED_API); + assertThat(res.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(res.getBody(), equalTo(expectedResponseFromLegacyPlugin)); + } + + private void assertOKResponseFromProtectedPlugin(TestRestClient client) { + String expectedResponseFromProtectedPlugin = "{\"response_string\":\"Hello from dummy protected plugin\"}"; + TestRestClient.HttpResponse res = client.get(PROTECTED_API); + assertThat(res.getStatusCode(), equalTo(HttpStatus.SC_OK)); + assertThat(res.getBody(), equalTo(expectedResponseFromProtectedPlugin)); + } +} diff --git a/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java b/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java index 4eaa49b62e..6b72a36277 100644 --- a/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java +++ b/src/integrationTest/java/org/opensearch/security/rest/WhoAmITests.java @@ -18,9 +18,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.opensearch.rest.RestRequest; -import org.opensearch.security.auditlog.AuditLog; -import org.opensearch.security.auditlog.impl.AuditCategory; import org.opensearch.security.auditlog.impl.AuditMessage; import org.opensearch.test.framework.AuditCompliance; import org.opensearch.test.framework.AuditConfiguration; @@ -47,12 +44,13 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.lessThan; import static org.junit.Assert.assertTrue; +import static org.opensearch.rest.RestRequest.Method.GET; import static org.opensearch.security.auditlog.impl.AuditCategory.GRANTED_PRIVILEGES; import static org.opensearch.security.auditlog.impl.AuditCategory.MISSING_PRIVILEGES; import static org.opensearch.test.framework.TestSecurityConfig.AuthcDomain.AUTHC_HTTPBASIC_INTERNAL; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.auditPredicate; import static org.opensearch.test.framework.audit.AuditMessagePredicate.grantedPrivilege; -import static org.opensearch.test.framework.audit.AuditMessagePredicate.userAuthenticated; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.privilegePredicateRESTLayer; +import static org.opensearch.test.framework.audit.AuditMessagePredicate.userAuthenticatedPredicate; @RunWith(com.carrotsearch.randomizedtesting.RandomizedRunner.class) @ThreadLeakScope(ThreadLeakScope.Scope.NONE) @@ -102,8 +100,8 @@ public void testWhoAmIWithGetPermissions() { assertResponse(client.get(WHOAMI_PROTECTED_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); // audit log, named route - assertExactlyOneAuthenticatedLogMessage(WHO_AM_I); - assertExactlyOnePrivilegeEvaluationMessage(GRANTED_PRIVILEGES, WHO_AM_I); + auditLogsRule.assertExactlyOne(userAuthenticatedPredicate(WHO_AM_I, GET, "/" + WHOAMI_PROTECTED_ENDPOINT)); + auditLogsRule.assertExactlyOne(privilegePredicateRESTLayer(GRANTED_PRIVILEGES, WHO_AM_I, GET, "/" + WHOAMI_PROTECTED_ENDPOINT)); assertResponse(client.get(WHOAMI_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); } @@ -115,8 +113,10 @@ public void testWhoAmIWithGetPermissionsLegacy() { assertResponse(client.get(WHOAMI_PROTECTED_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); // audit log, named route - assertExactlyOneAuthenticatedLogMessage(WHO_AM_I_LEGACY); - assertExactlyOnePrivilegeEvaluationMessage(GRANTED_PRIVILEGES, WHO_AM_I_LEGACY); + auditLogsRule.assertExactlyOne(userAuthenticatedPredicate(WHO_AM_I_LEGACY, GET, "/" + WHOAMI_PROTECTED_ENDPOINT)); + auditLogsRule.assertExactlyOne( + privilegePredicateRESTLayer(GRANTED_PRIVILEGES, WHO_AM_I_LEGACY, GET, "/" + WHOAMI_PROTECTED_ENDPOINT) + ); assertResponse(client.get(WHOAMI_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); } @@ -127,8 +127,10 @@ public void testWhoAmIWithoutGetPermissions() { try (TestRestClient client = cluster.getRestClient(WHO_AM_I_NO_PERM)) { assertResponse(client.get(WHOAMI_PROTECTED_ENDPOINT), HttpStatus.SC_UNAUTHORIZED, expectedUnuauthorizedBody); // audit log, named route - assertExactlyOneAuthenticatedLogMessage(WHO_AM_I_NO_PERM); - assertExactlyOnePrivilegeEvaluationMessage(MISSING_PRIVILEGES, WHO_AM_I_NO_PERM); + auditLogsRule.assertExactlyOne(userAuthenticatedPredicate(WHO_AM_I_NO_PERM, GET, "/" + WHOAMI_PROTECTED_ENDPOINT)); + auditLogsRule.assertExactlyOne( + privilegePredicateRESTLayer(MISSING_PRIVILEGES, WHO_AM_I_NO_PERM, GET, "/" + WHOAMI_PROTECTED_ENDPOINT) + ); assertResponse(client.get(WHOAMI_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); } @@ -152,13 +154,18 @@ public void testWhoAmIPost() { assertThat(client.post(WHOAMI_ENDPOINT).getStatusCode(), equalTo(HttpStatus.SC_OK)); } + // No audit logs generated because `/whoami` is passthrough at Transport Layer, and POST route is not a NamedRoute + auditLogsRule.assertAuditLogsCount(0, 0); } @Test public void testAuditLogSimilarityWithTransportLayer() { try (TestRestClient client = cluster.getRestClient(AUDIT_LOG_VERIFIER)) { assertResponse(client.get(WHOAMI_PROTECTED_ENDPOINT), HttpStatus.SC_OK, expectedAuthorizedBody); - assertExactlyOnePrivilegeEvaluationMessage(GRANTED_PRIVILEGES, AUDIT_LOG_VERIFIER); + auditLogsRule.assertExactlyOne(userAuthenticatedPredicate(AUDIT_LOG_VERIFIER, GET, "/" + WHOAMI_PROTECTED_ENDPOINT)); + auditLogsRule.assertExactlyOne( + privilegePredicateRESTLayer(GRANTED_PRIVILEGES, AUDIT_LOG_VERIFIER, GET, "/" + WHOAMI_PROTECTED_ENDPOINT) + ); assertThat(client.get("_cat/indices").getStatusCode(), equalTo(HttpStatus.SC_OK)); @@ -179,26 +186,6 @@ private void assertResponse(TestRestClient.HttpResponse response, int expectedSt assertThat(response.getBody(), equalTo(expectedBody)); } - private void assertExactlyOneAuthenticatedLogMessage(TestSecurityConfig.User user) { - auditLogsRule.assertExactly( - 1, - userAuthenticated(user).withLayer(AuditLog.Origin.REST) - .withRestMethod(RestRequest.Method.GET) - .withRequestPath("/" + WhoAmITests.WHOAMI_PROTECTED_ENDPOINT) - .withInitiatingUser(user) - ); - } - - private void assertExactlyOnePrivilegeEvaluationMessage(AuditCategory privileges, TestSecurityConfig.User user) { - auditLogsRule.assertExactly( - 1, - auditPredicate(privileges).withLayer(AuditLog.Origin.REST) - .withRestMethod(RestRequest.Method.GET) - .withRequestPath("/" + WhoAmITests.WHOAMI_PROTECTED_ENDPOINT) - .withEffectiveUser(user) - ); - } - private void verifyAuditLogSimilarity(List currentTestAuditMessages) { List restSet = new ArrayList<>(); List transportSet = new ArrayList<>(); diff --git a/src/integrationTest/java/org/opensearch/test/framework/audit/AuditMessagePredicate.java b/src/integrationTest/java/org/opensearch/test/framework/audit/AuditMessagePredicate.java index 922f2d54aa..4935bf0387 100644 --- a/src/integrationTest/java/org/opensearch/test/framework/audit/AuditMessagePredicate.java +++ b/src/integrationTest/java/org/opensearch/test/framework/audit/AuditMessagePredicate.java @@ -41,6 +41,7 @@ public class AuditMessagePredicate implements Predicate { private final String transportRequestType; private final String effectiveUser; private final String index; + private final String privilege; private AuditMessagePredicate( AuditCategory category, @@ -50,7 +51,8 @@ private AuditMessagePredicate( Method requestMethod, String transportRequestType, String effectiveUser, - String index + String index, + String privilege ) { this.category = category; this.requestLayer = requestLayer; @@ -60,10 +62,11 @@ private AuditMessagePredicate( this.transportRequestType = transportRequestType; this.effectiveUser = effectiveUser; this.index = index; + this.privilege = privilege; } private AuditMessagePredicate(AuditCategory category) { - this(category, null, null, null, null, null, null, null); + this(category, null, null, null, null, null, null, null, null); } public static AuditMessagePredicate auditPredicate(AuditCategory category) { @@ -82,6 +85,26 @@ public static AuditMessagePredicate missingPrivilege(User user, String requestTy return auditPredicate(MISSING_PRIVILEGES).withLayer(Origin.TRANSPORT).withEffectiveUser(user).withTransportRequestType(requestType); } + public static AuditMessagePredicate privilegePredicateTransportLayer( + AuditCategory category, + User user, + String requestType, + String privilege + ) { + return auditPredicate(category).withLayer(Origin.TRANSPORT) + .withEffectiveUser(user) + .withPrivilege(privilege) + .withTransportRequestType(requestType); + } + + public static AuditMessagePredicate privilegePredicateRESTLayer(AuditCategory category, User user, Method method, String endpoint) { + return auditPredicate(category).withLayer(Origin.REST).withEffectiveUser(user).withRestRequest(method, endpoint); + } + + public static AuditMessagePredicate userAuthenticatedPredicate(User user, Method method, String endpoint) { + return userAuthenticated(user).withLayer(Origin.REST).withRestRequest(method, endpoint).withInitiatingUser(user); + } + public AuditMessagePredicate withLayer(Origin layer) { return new AuditMessagePredicate( category, @@ -91,7 +114,8 @@ public AuditMessagePredicate withLayer(Origin layer) { requestMethod, transportRequestType, effectiveUser, - index + index, + privilege ); } @@ -104,7 +128,8 @@ public AuditMessagePredicate withRequestPath(String path) { requestMethod, transportRequestType, effectiveUser, - index + index, + privilege ); } @@ -117,7 +142,8 @@ public AuditMessagePredicate withInitiatingUser(String user) { requestMethod, transportRequestType, effectiveUser, - index + index, + privilege ); } @@ -134,7 +160,8 @@ public AuditMessagePredicate withRestMethod(Method method) { method, transportRequestType, effectiveUser, - index + index, + privilege ); } @@ -147,7 +174,8 @@ public AuditMessagePredicate withTransportRequestType(String type) { requestMethod, type, effectiveUser, - index + index, + privilege ); } @@ -160,7 +188,8 @@ public AuditMessagePredicate withEffectiveUser(String user) { requestMethod, transportRequestType, user, - index + index, + privilege ); } @@ -181,7 +210,22 @@ public AuditMessagePredicate withIndex(String indexName) { requestMethod, transportRequestType, effectiveUser, - indexName + indexName, + privilege + ); + } + + public AuditMessagePredicate withPrivilege(String privilegeAction) { + return new AuditMessagePredicate( + category, + requestLayer, + restRequestPath, + initiatingUser, + requestMethod, + transportRequestType, + effectiveUser, + index, + privilegeAction ); } @@ -196,6 +240,7 @@ public boolean test(AuditMessage auditMessage) { predicates.add(audit -> Objects.isNull(transportRequestType) || transportRequestType.equals(audit.getRequestType())); predicates.add(audit -> Objects.isNull(effectiveUser) || effectiveUser.equals(audit.getEffectiveUser())); predicates.add(audit -> Objects.isNull(index) || containIndex(audit, index)); + predicates.add(audit -> Objects.isNull(privilege) || privilege.equals(audit.getPrivilege())); return predicates.stream().reduce(Predicate::and).orElseThrow().test(auditMessage); } diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/AbstractRestHandler.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/AbstractRestHandler.java new file mode 100644 index 0000000000..764173b5b9 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/AbstractRestHandler.java @@ -0,0 +1,53 @@ +package org.opensearch.test.framework.testplugins; + +import org.opensearch.ExceptionsHelper; +import org.opensearch.client.node.NodeClient; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.XContentBuilder; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestRequest; + +import java.io.IOException; + +public class AbstractRestHandler extends BaseRestHandler { + + @Override + public String getName() { + return getClass().getSimpleName(); + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + switch (request.method()) { + case GET: + return channel -> handleGet(channel, request, client); + case POST: + return channel -> handlePost(channel, request, client); + default: + throw new IllegalArgumentException(request.method() + " not supported"); + } + } + + private void notImplemented(RestChannel channel, RestRequest.Method method) { + try { + final XContentBuilder builder = channel.newBuilder(); + builder.startObject(); + builder.field("status", RestStatus.NOT_IMPLEMENTED.name()); + builder.field("message", "Method " + method + " not implemented."); + builder.endObject(); + channel.sendResponse(new BytesRestResponse(RestStatus.NOT_IMPLEMENTED, builder)); + } catch (IOException e) { + throw ExceptionsHelper.convertToOpenSearchException(e); + } + } + + public void handlePost(RestChannel channel, RestRequest request, NodeClient client) { + notImplemented(channel, request.method()); + } + + public void handleGet(RestChannel channel, RestRequest request, NodeClient client) { + notImplemented(channel, request.method()); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/CustomLegacyTestPlugin.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/CustomLegacyTestPlugin.java new file mode 100644 index 0000000000..648abef704 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/CustomLegacyTestPlugin.java @@ -0,0 +1,61 @@ +/* + * 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.test.framework.testplugins.dummy; + +import org.opensearch.action.ActionRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.IndexScopedSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.ClusterPlugin; +import org.opensearch.plugins.NetworkPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestHandler; +import org.opensearch.test.framework.testplugins.dummy.dummyaction.DummyAction; +import org.opensearch.test.framework.testplugins.dummy.dummyaction.TransportDummyAction; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * Registers a plugin with legacy routes using {@link org.opensearch.rest.RestHandler.Route} + */ +public class CustomLegacyTestPlugin extends Plugin implements ClusterPlugin, NetworkPlugin, ActionPlugin { + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + final List handlers = new ArrayList(1); + handlers.add(new LegacyRestHandler()); + return handlers; + } + + @Override + public List> getActions() { + List> actions = new ArrayList<>(1); + actions.add(new ActionHandler<>(DummyAction.INSTANCE, TransportDummyAction.class)); + return actions; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/LegacyRestHandler.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/LegacyRestHandler.java new file mode 100644 index 0000000000..e001628596 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/LegacyRestHandler.java @@ -0,0 +1,56 @@ +/* + * 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.test.framework.testplugins.dummy; + +import com.google.common.collect.ImmutableList; +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestStatusToXContentListener; +import org.opensearch.test.framework.testplugins.AbstractRestHandler; +import org.opensearch.test.framework.testplugins.dummy.dummyaction.DummyAction; +import org.opensearch.test.framework.testplugins.dummy.dummyaction.DummyRequest; + +import java.util.List; + +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.POST; +import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; + +public class LegacyRestHandler extends AbstractRestHandler { + + private static final List routes = addRoutesPrefix( + ImmutableList.of(new Route(POST, "/dummy"), new Route(GET, "/dummy")), + "/_plugins/_dummy" + ); + + public LegacyRestHandler() { + super(); + } + + @Override + public List routes() { + return routes; + } + + @Override + public String getName() { + return "Dummy Rest Action"; + } + + @Override + public void handleGet(RestChannel channel, RestRequest request, NodeClient client) { + String message = request.param("message"); + DummyRequest dummyRequest = new DummyRequest(message); + client.execute(DummyAction.INSTANCE, dummyRequest, new RestStatusToXContentListener<>(channel)); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyAction.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyAction.java new file mode 100644 index 0000000000..ff10f0ca74 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyAction.java @@ -0,0 +1,24 @@ +/* + * 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.test.framework.testplugins.dummy.dummyaction; + +import org.opensearch.action.ActionType; + +public class DummyAction extends ActionType { + + public static final DummyAction INSTANCE = new DummyAction(); + public static final String NAME = "cluster:admin/dummy_plugin/dummy"; + + protected DummyAction() { + super(NAME, DummyResponse::new); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyRequest.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyRequest.java new file mode 100644 index 0000000000..5928b4892f --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyRequest.java @@ -0,0 +1,46 @@ +/* + * 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.test.framework.testplugins.dummy.dummyaction; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +public class DummyRequest extends ActionRequest implements ToXContent { + + private final String message; + + public DummyRequest(final StreamInput in) throws IOException { + super(in); + message = in.readString(); + } + + public DummyRequest(String message) { + this.message = message; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.field("message", message); + + return xContentBuilder; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyResponse.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyResponse.java new file mode 100644 index 0000000000..e2ee6c9344 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/DummyResponse.java @@ -0,0 +1,59 @@ +/* + * 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.test.framework.testplugins.dummy.dummyaction; + +import org.opensearch.common.xcontent.StatusToXContentObject; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +public class DummyResponse extends ActionResponse implements StatusToXContentObject { + private final String responseString; + + public DummyResponse(String responseString) { + this.responseString = responseString; + } + + public DummyResponse(StreamInput in) throws IOException { + super(in); + responseString = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(responseString); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("response_string", responseString); + builder.endObject(); + return builder; + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } + + @Override + public RestStatus status() { + return RestStatus.OK; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/TransportDummyAction.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/TransportDummyAction.java new file mode 100644 index 0000000000..7e30af64bd --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummy/dummyaction/TransportDummyAction.java @@ -0,0 +1,33 @@ +/* + * 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.test.framework.testplugins.dummy.dummyaction; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +public class TransportDummyAction extends HandledTransportAction { + + @Inject + public TransportDummyAction(final TransportService transportService, final ActionFilters actionFilters) { + super(DummyAction.NAME, transportService, actionFilters, DummyRequest::new); + } + + @Override + protected void doExecute(Task task, DummyRequest request, ActionListener listener) { + String responseString = "Hello from dummy plugin"; + listener.onResponse(new DummyResponse(responseString)); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/CustomRestProtectedTestPlugin.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/CustomRestProtectedTestPlugin.java new file mode 100644 index 0000000000..780bee4ac6 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/CustomRestProtectedTestPlugin.java @@ -0,0 +1,62 @@ +/* + * 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.test.framework.testplugins.dummyprotected; + +import org.opensearch.action.ActionRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.node.DiscoveryNodes; +import org.opensearch.common.settings.ClusterSettings; +import org.opensearch.common.settings.IndexScopedSettings; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.settings.SettingsFilter; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.plugins.ActionPlugin; +import org.opensearch.plugins.ClusterPlugin; +import org.opensearch.plugins.NetworkPlugin; +import org.opensearch.plugins.Plugin; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestHandler; +import org.opensearch.test.framework.testplugins.dummyprotected.dummyaction.DummyAction; +import org.opensearch.test.framework.testplugins.dummyprotected.dummyaction.TransportDummyAction; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * Registers a plugin with protected routes using {@linkplain org.opensearch.rest.NamedRoute} + * This allows authorization against REST layer + */ +public class CustomRestProtectedTestPlugin extends Plugin implements ClusterPlugin, NetworkPlugin, ActionPlugin { + + @Override + public List getRestHandlers( + Settings settings, + RestController restController, + ClusterSettings clusterSettings, + IndexScopedSettings indexScopedSettings, + SettingsFilter settingsFilter, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier nodesInCluster + ) { + final List handlers = new ArrayList(1); + handlers.add(new ProtectedRoutesRestHandler()); + return handlers; + } + + @Override + public List> getActions() { + List> actions = new ArrayList<>(1); + actions.add(new ActionHandler<>(DummyAction.INSTANCE, TransportDummyAction.class)); + return actions; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/ProtectedRoutesRestHandler.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/ProtectedRoutesRestHandler.java new file mode 100644 index 0000000000..5f20585cd6 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/ProtectedRoutesRestHandler.java @@ -0,0 +1,70 @@ +/* + * 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.test.framework.testplugins.dummyprotected; + +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableList; + +import org.opensearch.client.node.NodeClient; +import org.opensearch.rest.NamedRoute; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.action.RestStatusToXContentListener; +import org.opensearch.test.framework.testplugins.AbstractRestHandler; +import org.opensearch.test.framework.testplugins.dummyprotected.dummyaction.DummyAction; +import org.opensearch.test.framework.testplugins.dummyprotected.dummyaction.DummyRequest; + +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.POST; +import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; + +public class ProtectedRoutesRestHandler extends AbstractRestHandler { + + private static final List routes = addRoutesPrefix( + ImmutableList.of( + new NamedRoute.Builder().method(POST) + .path("/dummy") + .uniqueName("security:dummy_protected/post") + .legacyActionNames(Set.of("cluster:admin/dummy_protected_plugin/dummy/post")) + .build(), + new NamedRoute.Builder().method(GET) + .path("/dummy") + .uniqueName("security:dummy_protected/get") + .legacyActionNames(Set.of("cluster:admin/dummy_protected_plugin/dummy/get")) + .build() + ), + "/_plugins/_dummy_protected" + ); + + public ProtectedRoutesRestHandler() { + super(); + } + + @Override + public List routes() { + return routes; + } + + @Override + public String getName() { + return "Dummy Protected Rest Action"; + } + + @Override + public void handleGet(RestChannel channel, RestRequest request, NodeClient client) { + String message = request.param("message"); + DummyRequest dummyRequest = new DummyRequest(message); + client.execute(DummyAction.INSTANCE, dummyRequest, new RestStatusToXContentListener<>(channel)); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyAction.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyAction.java new file mode 100644 index 0000000000..26edfa4e17 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyAction.java @@ -0,0 +1,24 @@ +/* + * 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.test.framework.testplugins.dummyprotected.dummyaction; + +import org.opensearch.action.ActionType; + +public class DummyAction extends ActionType { + + public static final DummyAction INSTANCE = new DummyAction(); + public static final String NAME = "cluster:admin/dummy_protected_plugin/dummy/get"; + + protected DummyAction() { + super(NAME, DummyResponse::new); + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyRequest.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyRequest.java new file mode 100644 index 0000000000..025d2e1c55 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyRequest.java @@ -0,0 +1,45 @@ +/* + * 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.test.framework.testplugins.dummyprotected.dummyaction; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.ActionRequestValidationException; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.xcontent.ToXContent; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +public class DummyRequest extends ActionRequest implements ToXContent { + + private final String message; + + public DummyRequest(final StreamInput in) throws IOException { + super(in); + message = in.readString(); + } + + public DummyRequest(String message) { + this.message = message; + } + + @Override + public ActionRequestValidationException validate() { + return null; + } + + @Override + public XContentBuilder toXContent(XContentBuilder xContentBuilder, Params params) throws IOException { + xContentBuilder.field("message", message); + return xContentBuilder; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyResponse.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyResponse.java new file mode 100644 index 0000000000..efd7be49b4 --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/DummyResponse.java @@ -0,0 +1,59 @@ +/* + * 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.test.framework.testplugins.dummyprotected.dummyaction; + +import org.opensearch.common.xcontent.StatusToXContentObject; +import org.opensearch.core.action.ActionResponse; +import org.opensearch.core.common.Strings; +import org.opensearch.core.common.io.stream.StreamInput; +import org.opensearch.core.common.io.stream.StreamOutput; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.core.xcontent.MediaTypeRegistry; +import org.opensearch.core.xcontent.XContentBuilder; + +import java.io.IOException; + +public class DummyResponse extends ActionResponse implements StatusToXContentObject { + private final String responseString; + + public DummyResponse(String responseString) { + this.responseString = responseString; + } + + public DummyResponse(StreamInput in) throws IOException { + super(in); + responseString = in.readString(); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + out.writeString(responseString); + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("response_string", this.responseString); + builder.endObject(); + return builder; + } + + @Override + public String toString() { + return Strings.toString(MediaTypeRegistry.JSON, this, true, true); + } + + @Override + public RestStatus status() { + return RestStatus.OK; + } +} diff --git a/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/TransportDummyAction.java b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/TransportDummyAction.java new file mode 100644 index 0000000000..05a10d875a --- /dev/null +++ b/src/integrationTest/java/org/opensearch/test/framework/testplugins/dummyprotected/dummyaction/TransportDummyAction.java @@ -0,0 +1,33 @@ +/* + * 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.test.framework.testplugins.dummyprotected.dummyaction; + +import org.opensearch.action.support.ActionFilters; +import org.opensearch.action.support.HandledTransportAction; +import org.opensearch.common.inject.Inject; +import org.opensearch.core.action.ActionListener; +import org.opensearch.tasks.Task; +import org.opensearch.transport.TransportService; + +public class TransportDummyAction extends HandledTransportAction { + + @Inject + public TransportDummyAction(final TransportService transportService, final ActionFilters actionFilters) { + super(DummyAction.NAME, transportService, actionFilters, DummyRequest::new); + } + + @Override + protected void doExecute(Task task, DummyRequest request, ActionListener listener) { + String responseString = "Hello from dummy protected plugin"; + listener.onResponse(new DummyResponse(responseString)); + } +}