From e9d73224d83a8781be53a0f2d1a1796d56fe1267 Mon Sep 17 00:00:00 2001 From: Bharathwaj G <58062316+bharath-techie@users.noreply.github.com> Date: Fri, 7 Oct 2022 21:21:50 +0530 Subject: [PATCH] Point in time security changes (#2094) Description For 'Delete PIT' and 'PIT segments' API, when PIT IDs are passed as part of request, this custom evaluator decode the PITs to indices and resolve the indices with user permissions. If user has permission to all indices of PIT, then PIT is permitted to the user. Only when the user has permissions for all PITs in the request, then we allow the operation. For requests which operates on 'all' PITs, we skip the custom evaluator and evaluate via standard code Alias and data stream behavior : PIT IDs always contain the resolved indices ( underlying indices ) when saved. Based on this, For alias, user must have either 'index' or 'alias' permission for any PIT operation. For data stream, user must have both 'data stream' AND 'backing indices of data stream' permission ( eg : data-stream-11 + .ds-my-data-stream11-000001 ) for any PIT operation. With just data stream permission, user will be able to create pit but will not be able to use the PIT ID for other operations such as search without backing indices permission. Signed-off-by: Bharathwaj G Signed-off-by: Bharathwaj G <58062316+bharath-techie@users.noreply.github.com> (cherry picked from commit 207cfcc379ffd4127e32b9fdfdd75ea394b48d0e) --- config/roles.yml | 9 + .../security/OpenSearchSecurityPlugin.java | 8 +- .../privileges/PitPrivilegesEvaluator.java | 105 ++++++++ .../privileges/PrivilegesEvaluator.java | 8 + .../resolver/IndexResolverReplacer.java | 10 +- .../static_config/static_action_groups.yml | 7 +- .../security/PitIntegrationTests.java | 243 ++++++++++++++++++ .../security/test/helper/rest/RestHelper.java | 19 ++ src/test/resources/internal_users.yml | 9 + src/test/resources/roles.yml | 41 +++ src/test/resources/roles_mapping.yml | 15 ++ 11 files changed, 465 insertions(+), 9 deletions(-) create mode 100644 src/main/java/org/opensearch/security/privileges/PitPrivilegesEvaluator.java create mode 100644 src/test/java/org/opensearch/security/PitIntegrationTests.java diff --git a/config/roles.yml b/config/roles.yml index 721349c086..c96e8b27e9 100644 --- a/config/roles.yml +++ b/config/roles.yml @@ -246,3 +246,12 @@ snapshot_management_read_access: - 'cluster:admin/opensearch/snapshot_management/policy/explain' - 'cluster:admin/repository/get' - 'cluster:admin/snapshot/get' + +# Allows user to use point in time functionality +point_in_time_full_access: + reserved: true + index_permissions: + - index_patterns: + - '*' + allowed_actions: + - 'manage_point_in_time' diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 66530cfaed..add4a285bd 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -66,6 +66,7 @@ import org.opensearch.Version; import org.opensearch.action.ActionRequest; import org.opensearch.action.ActionResponse; +import org.opensearch.action.search.PitService; import org.opensearch.action.search.SearchScrollAction; import org.opensearch.action.support.ActionFilter; import org.opensearch.client.Client; @@ -1161,12 +1162,15 @@ public static class GuiceHolder implements LifecycleComponent { private static RemoteClusterService remoteClusterService; private static IndicesService indicesService; + private static PitService pitService; + @Inject public GuiceHolder(final RepositoriesService repositoriesService, - final TransportService remoteClusterService, IndicesService indicesService) { + final TransportService remoteClusterService, IndicesService indicesService, PitService pitService) { GuiceHolder.repositoriesService = repositoriesService; GuiceHolder.remoteClusterService = remoteClusterService.getRemoteClusterService(); GuiceHolder.indicesService = indicesService; + GuiceHolder.pitService = pitService; } public static RepositoriesService getRepositoriesService() { @@ -1180,6 +1184,8 @@ public static RemoteClusterService getRemoteClusterService() { public static IndicesService getIndicesService() { return indicesService; } + + public static PitService getPitService() { return pitService; } @Override public void close() { diff --git a/src/main/java/org/opensearch/security/privileges/PitPrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PitPrivilegesEvaluator.java new file mode 100644 index 0000000000..b146365d57 --- /dev/null +++ b/src/main/java/org/opensearch/security/privileges/PitPrivilegesEvaluator.java @@ -0,0 +1,105 @@ +/* + * 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.privileges; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.opensearch.action.ActionRequest; +import org.opensearch.action.admin.indices.segments.PitSegmentsRequest; +import org.opensearch.action.search.CreatePitRequest; +import org.opensearch.action.search.DeletePitRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.unit.TimeValue; +import org.opensearch.security.OpenSearchSecurityPlugin; +import org.opensearch.security.resolver.IndexResolverReplacer; +import org.opensearch.security.securityconf.SecurityRoles; +import org.opensearch.security.user.User; + + +/** + * This class evaluates privileges for point in time (Delete and List all) operations. + * For aliases - users must have either alias permission or backing index permissions + * For data streams - users must have access to backing indices permission + data streams permission. + */ +public class PitPrivilegesEvaluator { + + public PrivilegesEvaluatorResponse evaluate(final ActionRequest request, final ClusterService clusterService, + final User user, final SecurityRoles securityRoles, final String action, + final IndexNameExpressionResolver resolver, + final PrivilegesEvaluatorResponse presponse, + final IndexResolverReplacer irr) { + + if(!(request instanceof DeletePitRequest || request instanceof PitSegmentsRequest)) { + return presponse; + } + List pitIds = new ArrayList<>(); + + if (request instanceof DeletePitRequest) { + DeletePitRequest deletePitRequest = (DeletePitRequest) request; + pitIds = deletePitRequest.getPitIds(); + } else if(request instanceof PitSegmentsRequest) { + PitSegmentsRequest pitSegmentsRequest = (PitSegmentsRequest) request; + pitIds = pitSegmentsRequest.getPitIds(); + } + // if request is for all PIT IDs, skip custom pit ids evaluation + if (pitIds.size() == 1 && "_all".equals(pitIds.get(0))) { + return presponse; + } else { + return handlePitsAccess(pitIds, clusterService, user, securityRoles, + action, resolver, presponse, irr); + } + } + + /** + * Handle access for delete operation / pit segments operation where PIT IDs are explicitly passed + */ + private PrivilegesEvaluatorResponse handlePitsAccess(List pitIds, ClusterService clusterService, + User user, SecurityRoles securityRoles, final String action, + IndexNameExpressionResolver resolver, PrivilegesEvaluatorResponse presponse, + final IndexResolverReplacer irr) { + Map pitToIndicesMap = OpenSearchSecurityPlugin. + GuiceHolder.getPitService().getIndicesForPits(pitIds); + Set pitIndices = new HashSet<>(); + // add indices across all PITs to a set and evaluate if user has access to all indices + for(String[] indices: pitToIndicesMap.values()) { + pitIndices.addAll(Arrays.asList(indices)); + } + Set allPermittedIndices = getPermittedIndices(pitIndices, clusterService, user, + securityRoles, action, resolver, irr); + // Only if user has access to all PIT's indices, allow operation, otherwise continue evaluation in PrivilegesEvaluator. + if(allPermittedIndices.containsAll(pitIndices)) { + presponse.allowed = true; + presponse.markComplete(); + } + return presponse; + } + + /** + * This method returns list of permitted indices for the PIT indices passed + */ + private Set getPermittedIndices(Set pitIndices, ClusterService clusterService, + User user, SecurityRoles securityRoles, final String action, + IndexNameExpressionResolver resolver, final IndexResolverReplacer irr) { + String[] indicesArr = new String[pitIndices.size()]; + CreatePitRequest req = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true, + pitIndices.toArray(indicesArr)); + final IndexResolverReplacer.Resolved pitResolved = irr.resolveRequest(req); + return securityRoles.reduce(pitResolved, + user, new String[]{action}, resolver, clusterService); + } +} diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index fd1b26d388..df9c432827 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -130,6 +130,7 @@ public class PrivilegesEvaluator { private final SecurityIndexAccessEvaluator securityIndexAccessEvaluator; private final ProtectedIndexAccessEvaluator protectedIndexAccessEvaluator; private final TermsAggregationEvaluator termsAggregationEvaluator; + private final PitPrivilegesEvaluator pitPrivilegesEvaluator; private final boolean dlsFlsEnabled; private final boolean dfmEmptyOverwritesAll; private DynamicConfigModel dcm; @@ -158,6 +159,7 @@ public PrivilegesEvaluator(final ClusterService clusterService, final ThreadPool securityIndexAccessEvaluator = new SecurityIndexAccessEvaluator(settings, auditLog, irr); protectedIndexAccessEvaluator = new ProtectedIndexAccessEvaluator(settings, auditLog); termsAggregationEvaluator = new TermsAggregationEvaluator(); + pitPrivilegesEvaluator = new PitPrivilegesEvaluator(); this.namedXContentRegistry = namedXContentRegistry; this.dlsFlsEnabled = dlsFlsEnabled; this.dfmEmptyOverwritesAll = settings.getAsBoolean(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, false); @@ -282,6 +284,12 @@ public PrivilegesEvaluatorResponse evaluate(final User user, String action0, fin return presponse; } + // check access for point in time requests + if(pitPrivilegesEvaluator.evaluate(request, clusterService, user, securityRoles, + action0, resolver, presponse, irr).isComplete()) { + return presponse; + } + final boolean dnfofEnabled = dcm.isDnfofEnabled(); final boolean isTraceEnabled = log.isTraceEnabled(); diff --git a/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java b/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java index e0eddf9993..d2d0685860 100644 --- a/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java +++ b/src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java @@ -370,11 +370,11 @@ public final static class Resolved { private final boolean isLocalAll; private final IndicesOptions indicesOptions; - private Resolved(final ImmutableSet aliases, - final ImmutableSet allIndices, - final ImmutableSet originalRequested, - final ImmutableSet remoteIndices, - IndicesOptions indicesOptions) { + public Resolved(final ImmutableSet aliases, + final ImmutableSet allIndices, + final ImmutableSet originalRequested, + final ImmutableSet remoteIndices, + IndicesOptions indicesOptions) { this.aliases = aliases; this.allIndices = allIndices; this.originalRequested = originalRequested; diff --git a/src/main/resources/static_config/static_action_groups.yml b/src/main/resources/static_config/static_action_groups.yml index d0ce7613a2..c7c351d171 100644 --- a/src/main/resources/static_config/static_action_groups.yml +++ b/src/main/resources/static_config/static_action_groups.yml @@ -233,8 +233,9 @@ manage_point_in_time: static: true allowed_actions: - "indices:data/read/point_in_time/create" - - "cluster:admin/point_in_time/delete" - - "cluster:admin/point_in_time/read*" + - "indices:data/read/point_in_time/delete" + - "indices:data/read/point_in_time/readall" + - "indices:data/read/search" - "indices:monitor/point_in_time/segments" - type: "cluster" + type: "index" description: "Manage point in time actions" diff --git a/src/test/java/org/opensearch/security/PitIntegrationTests.java b/src/test/java/org/opensearch/security/PitIntegrationTests.java new file mode 100644 index 0000000000..b31450dcf7 --- /dev/null +++ b/src/test/java/org/opensearch/security/PitIntegrationTests.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; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.HttpStatus; +import org.junit.Assert; +import org.junit.Test; + +import org.opensearch.action.admin.indices.alias.Alias; +import org.opensearch.action.admin.indices.create.CreateIndexRequest; +import org.opensearch.action.index.IndexRequest; +import org.opensearch.action.support.WriteRequest; +import org.opensearch.client.Client; +import org.opensearch.common.xcontent.XContentType; +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.rest.RestHelper; + +/** + * Integration tests to test point in time APIs permission model + */ +public class PitIntegrationTests extends SingleClusterTest { + + @Test + public void testPitExplicitAPIAccess() throws Exception { + setup(); + RestHelper rh = nonSslRestHelper(); + try (Client tc = getClient()) { + // create alias + tc.admin().indices().create(new CreateIndexRequest("pit_1") + .alias(new Alias("alias"))) + .actionGet(); + // create index + tc.index(new IndexRequest("pit_2").id("2").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE). + source("{\"content\":2}", XContentType.JSON)).actionGet(); + + } + + RestHelper.HttpResponse resc; + + // Create point in time in index should be successful since the user has permission for index + resc = rh.executePostRequest("/alias/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + String pitId1 = resc.findValueInJson("pit_id"); + + // Create point in time in index for which the user does not have permission + resc = rh.executePostRequest("/pit_2/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Create point in time in index for which the user has permission for + resc = rh.executePostRequest("/pit_2/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-2", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + String pitId2 = resc.findValueInJson("pit_id"); + resc = rh.executePostRequest("/pit*/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("all-pit", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + // PIT segments should work since there is atleast one PIT for which user has access for + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":\"" + pitId1 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + // PIT segments should work since there is atleast one PIT for which user has access for + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":\"" + pitId1 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + // Should throw error since user does not have access for pitId2 + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":\"" + pitId2 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Should throw error since user does not have access for pitId2 + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":[\"" + pitId1 +"\",\"" + pitId2 + "\"]}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Delete explicit PITs should work for PIT for which user has access for + resc = rh.executeDeleteRequest("/_search/point_in_time", + "{\"pit_id\":\"" + pitId1 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + Assert.assertEquals(pitId1, resc.findValueInJson("pits[0].pit_id")); + Assert.assertEquals("true", resc.findValueInJson("pits[0].successful")); + + // Should throw error since user does not have access for pitId2 + resc = rh.executeDeleteRequest("/_search/point_in_time", + "{\"pit_id\":\"" + pitId2 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Should throw error since user does not have access for pitId2 + resc = rh.executeDeleteRequest("/_search/point_in_time", + "{\"pit_id\":[\"" + pitId1 +"\",\"" + pitId2 + "\"]}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Delete explicit PITs should work for PIT for which user has access for + resc = rh.executeDeleteRequest("/_search/point_in_time", + "{\"pit_id\":\"" + pitId2 +"\"}", + encodeBasicHeader("pit-2", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + Assert.assertEquals(pitId2, resc.findValueInJson("pits[0].pit_id")); + Assert.assertEquals("true", resc.findValueInJson("pits[0].successful")); + + } + + @Test + public void testPitAllAPIAccess() throws Exception { + setup(); + RestHelper rh = nonSslRestHelper(); + + // Create two indices + try (Client tc = getClient()) { + tc.index(new IndexRequest("pit_1").id("1").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE). + source("{\"content\":1}", XContentType.JSON)).actionGet(); + tc.index(new IndexRequest("pit_2").id("2").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE). + source("{\"content\":2}", XContentType.JSON)).actionGet(); + } + + RestHelper.HttpResponse resc; + + // Create point in time in index should be successful since the user has permission for index + resc = rh.executePostRequest("/pit_1/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + String pitId1 = resc.findValueInJson("pit_id"); + + // Create point in time in index for which the user does not have permission + resc = rh.executePostRequest("/pit_2/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Create point in time in index for which the user has permission for + resc = rh.executePostRequest("/pit_2/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-2", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + String pitId2 = resc.findValueInJson("pit_id"); + + // Throw security error if user does not have all index permission + resc = rh.executeGetRequest("/_search/point_in_time/_all", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // List all PITs should work for user with all index access + resc = rh.executeGetRequest("/_search/point_in_time/_all", + encodeBasicHeader("all-pit", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + List pitList = new ArrayList<>(); + pitList.add(pitId1); + pitList.add(pitId2); + pitList.contains(resc.findValueInJson("pits[0].pit_id")); + pitList.contains(resc.findValueInJson("pits[1].pit_id")); + + // Throw security error if user does not have all index permission + resc = rh.executeGetRequest("/_cat/pit_segments/_all", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // PIT segments should work for user with all index access + resc = rh.executeGetRequest("/_cat/pit_segments/_all", + encodeBasicHeader("all-pit", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + + // Throw security error if user does not have all index permission + resc = rh.executeDeleteRequest("/_search/point_in_time/_all", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Delete all PITs should work for user with all index access + resc = rh.executeDeleteRequest("/_search/point_in_time/_all", + encodeBasicHeader("all-pit", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + pitList.contains(resc.findValueInJson("pits[0].pit_id")); + pitList.contains(resc.findValueInJson("pits[1].pit_id")); + Assert.assertEquals("true", resc.findValueInJson("pits[0].successful")); + Assert.assertEquals("true", resc.findValueInJson("pits[1].successful")); + + } + + @Test + public void testDataStreamWithPits() throws Exception { + setup(); + RestHelper rh = nonSslRestHelper(); + String indexTemplate = "{\"index_patterns\": [ \"my-data-stream*\" ], \"data_stream\": { }, \"priority\": 200, " + + "\"template\": {\"settings\": { } } }"; + + rh.executePutRequest("/_index_template/my-data-stream-template", indexTemplate, encodeBasicHeader("ds1", "nagilum")); + + rh.executePutRequest("/_data_stream/my-data-stream11", indexTemplate, encodeBasicHeader("ds3", "nagilum")); + rh.executePutRequest("/_data_stream/my-data-stream21", indexTemplate, encodeBasicHeader("ds3", "nagilum")); + + RestHelper.HttpResponse resc; + // create pit should work since user has permission on data stream + resc = rh.executePostRequest("/my-data-stream11/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + String pitId1 = resc.findValueInJson("pit_id"); + + // PIT segments works since the user has access for backing indices + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":\"" + pitId1 +"\"}", + encodeBasicHeader("pit-1", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + + // create pit should work since user has permission on data stream + resc = rh.executePostRequest("/my-data-stream21/_search/point_in_time?keep_alive=100m", "", + encodeBasicHeader("pit-2", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + String pitId2 = resc.findValueInJson("pit_id"); + + // since pit-2 doesn't have permission to backing data stream indices, throw security error + resc = rh.executeGetRequest("/_cat/pit_segments", + "{\"pit_id\":\"" + pitId2 +"\"}", + encodeBasicHeader("pit-2", "nagilum")); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, resc.getStatusCode()); + + // Delete all PITs should work for user with all index access + resc = rh.executeDeleteRequest("/_search/point_in_time/_all", + encodeBasicHeader("all-pit", "nagilum")); + Assert.assertEquals(HttpStatus.SC_OK, resc.getStatusCode()); + } +} diff --git a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java index 6bc8a056b8..49d498833e 100644 --- a/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java +++ b/src/test/java/org/opensearch/security/test/helper/rest/RestHelper.java @@ -47,6 +47,7 @@ import org.apache.commons.io.IOUtils; import org.apache.http.Header; import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; @@ -59,6 +60,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; import org.apache.http.config.SocketConfig; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; @@ -143,6 +145,14 @@ public HttpResponse[] executeMultipleAsyncPutRequest(final int numOfRequests, fi public HttpResponse executeGetRequest(final String request, Header... header) { return executeRequest(new HttpGet(getHttpServerUri() + "/" + request), header); } + + public HttpResponse executeGetRequest(final String request, String body, Header... header) { + HttpUriRequest uriRequest = RequestBuilder.get(getHttpServerUri() + "/" + request) + .setEntity(createStringEntity(body)) + .setHeader(HttpHeaders.CONTENT_TYPE, "application/json") + .build(); + return executeRequest(uriRequest, header); + } public HttpResponse executeHeadRequest(final String request, Header... header) { return executeRequest(new HttpHead(getHttpServerUri() + "/" + request), header); @@ -164,6 +174,15 @@ public HttpResponse executeDeleteRequest(final String request, Header... header) return executeRequest(new HttpDelete(getHttpServerUri() + "/" + request), header); } + public HttpResponse executeDeleteRequest(final String request, String body, Header... header) { + HttpUriRequest uriRequest = RequestBuilder.delete(getHttpServerUri() + "/" + request) + .setEntity(createStringEntity(body)) + .setHeader(HttpHeaders.CONTENT_TYPE, "application/json") + .build(); + return executeRequest(uriRequest, header); + } + + public HttpResponse executePostRequest(final String request, String body, Header... header) { HttpPost uriRequest = new HttpPost(getHttpServerUri() + "/" + request); if (body != null && !body.isEmpty()) { diff --git a/src/test/resources/internal_users.yml b/src/test/resources/internal_users.yml index 99d821ce33..44464c9cf7 100644 --- a/src/test/resources/internal_users.yml +++ b/src/test/resources/internal_users.yml @@ -346,6 +346,15 @@ ds2: ds3: hash: $2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m #password is: nagilum +pit-1: + hash: $2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m + #password is: nagilum +pit-2: + hash: $2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m + #password is: nagilum +all-pit: + hash: $2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m + #password is: nagilum hidden_test: hash: $2a$12$n5nubfWATfQjSYHiWtUyeOxMIxFInUHOAx8VMmGmxFNPGpaBmeB.m opendistro_security_roles: diff --git a/src/test/resources/roles.yml b/src/test/resources/roles.yml index 20e7c38cdb..4da098e762 100644 --- a/src/test/resources/roles.yml +++ b/src/test/resources/roles.yml @@ -1129,6 +1129,47 @@ data_stream_3: allowed_actions: - "DATASTREAM_ALL" +point_in_time_1: + reserved: true + hidden: false + description: "Migrated from v6 (all types mapped)" + index_permissions: + - index_patterns: + - "pit_1" + - "*my-data-stream11*" + dls: null + fls: null + masked_fields: null + allowed_actions: + - "manage_point_in_time" + +point_in_time_2: + reserved: true + hidden: false + description: "Migrated from v6 (all types mapped)" + index_permissions: + - index_patterns: + - "my-data-stream21" + - "pit_2" + dls: null + fls: null + masked_fields: null + allowed_actions: + - "manage_point_in_time" + +point_in_time_all: + reserved: true + hidden: false + description: "Migrated from v6 (all types mapped)" + index_permissions: + - index_patterns: + - "*" + dls: null + fls: null + masked_fields: null + allowed_actions: + - "manage_point_in_time" + hidden_test: cluster_permissions: - SGS_CLUSTER_COMPOSITE_OPS diff --git a/src/test/resources/roles_mapping.yml b/src/test/resources/roles_mapping.yml index 9253b0c970..bc32c5b403 100644 --- a/src/test/resources/roles_mapping.yml +++ b/src/test/resources/roles_mapping.yml @@ -413,6 +413,21 @@ data_stream_3: hidden: false users: - "ds3" +point_in_time_1: + reserved: false + hidden: false + users: + - "pit-1" +point_in_time_2: + reserved: false + hidden: false + users: + - "pit-2" +point_in_time_all: + reserved: false + hidden: false + users: + - "all-pit" sem-role: reserved: false hidden: false