Skip to content

Commit 8834cd2

Browse files
authored
Merge branch '2.4' into backport/backport-2192-to-2.4
2 parents bde75d8 + 2a1e7d7 commit 8834cd2

10 files changed

Lines changed: 456 additions & 9 deletions

File tree

src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.opensearch.Version;
6767
import org.opensearch.action.ActionRequest;
6868
import org.opensearch.action.ActionResponse;
69+
import org.opensearch.action.search.PitService;
6970
import org.opensearch.action.search.SearchScrollAction;
7071
import org.opensearch.action.support.ActionFilter;
7172
import org.opensearch.client.Client;
@@ -1161,12 +1162,15 @@ public static class GuiceHolder implements LifecycleComponent {
11611162
private static RemoteClusterService remoteClusterService;
11621163
private static IndicesService indicesService;
11631164

1165+
private static PitService pitService;
1166+
11641167
@Inject
11651168
public GuiceHolder(final RepositoriesService repositoriesService,
1166-
final TransportService remoteClusterService, IndicesService indicesService) {
1169+
final TransportService remoteClusterService, IndicesService indicesService, PitService pitService) {
11671170
GuiceHolder.repositoriesService = repositoriesService;
11681171
GuiceHolder.remoteClusterService = remoteClusterService.getRemoteClusterService();
11691172
GuiceHolder.indicesService = indicesService;
1173+
GuiceHolder.pitService = pitService;
11701174
}
11711175

11721176
public static RepositoriesService getRepositoriesService() {
@@ -1180,6 +1184,8 @@ public static RemoteClusterService getRemoteClusterService() {
11801184
public static IndicesService getIndicesService() {
11811185
return indicesService;
11821186
}
1187+
1188+
public static PitService getPitService() { return pitService; }
11831189

11841190
@Override
11851191
public void close() {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*
8+
* Modifications Copyright OpenSearch Contributors. See
9+
* GitHub history for details.
10+
*/
11+
package org.opensearch.security.privileges;
12+
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.HashSet;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.concurrent.TimeUnit;
20+
21+
import org.opensearch.action.ActionRequest;
22+
import org.opensearch.action.admin.indices.segments.PitSegmentsRequest;
23+
import org.opensearch.action.search.CreatePitRequest;
24+
import org.opensearch.action.search.DeletePitRequest;
25+
import org.opensearch.cluster.metadata.IndexNameExpressionResolver;
26+
import org.opensearch.cluster.service.ClusterService;
27+
import org.opensearch.common.unit.TimeValue;
28+
import org.opensearch.security.OpenSearchSecurityPlugin;
29+
import org.opensearch.security.resolver.IndexResolverReplacer;
30+
import org.opensearch.security.securityconf.SecurityRoles;
31+
import org.opensearch.security.user.User;
32+
33+
34+
/**
35+
* This class evaluates privileges for point in time (Delete and List all) operations.
36+
* For aliases - users must have either alias permission or backing index permissions
37+
* For data streams - users must have access to backing indices permission + data streams permission.
38+
*/
39+
public class PitPrivilegesEvaluator {
40+
41+
public PrivilegesEvaluatorResponse evaluate(final ActionRequest request, final ClusterService clusterService,
42+
final User user, final SecurityRoles securityRoles, final String action,
43+
final IndexNameExpressionResolver resolver,
44+
final PrivilegesEvaluatorResponse presponse,
45+
final IndexResolverReplacer irr) {
46+
47+
if(!(request instanceof DeletePitRequest || request instanceof PitSegmentsRequest)) {
48+
return presponse;
49+
}
50+
List<String> pitIds = new ArrayList<>();
51+
52+
if (request instanceof DeletePitRequest) {
53+
DeletePitRequest deletePitRequest = (DeletePitRequest) request;
54+
pitIds = deletePitRequest.getPitIds();
55+
} else if(request instanceof PitSegmentsRequest) {
56+
PitSegmentsRequest pitSegmentsRequest = (PitSegmentsRequest) request;
57+
pitIds = pitSegmentsRequest.getPitIds();
58+
}
59+
// if request is for all PIT IDs, skip custom pit ids evaluation
60+
if (pitIds.size() == 1 && "_all".equals(pitIds.get(0))) {
61+
return presponse;
62+
} else {
63+
return handlePitsAccess(pitIds, clusterService, user, securityRoles,
64+
action, resolver, presponse, irr);
65+
}
66+
}
67+
68+
/**
69+
* Handle access for delete operation / pit segments operation where PIT IDs are explicitly passed
70+
*/
71+
private PrivilegesEvaluatorResponse handlePitsAccess(List<String> pitIds, ClusterService clusterService,
72+
User user, SecurityRoles securityRoles, final String action,
73+
IndexNameExpressionResolver resolver, PrivilegesEvaluatorResponse presponse,
74+
final IndexResolverReplacer irr) {
75+
Map<String, String[]> pitToIndicesMap = OpenSearchSecurityPlugin.
76+
GuiceHolder.getPitService().getIndicesForPits(pitIds);
77+
Set<String> pitIndices = new HashSet<>();
78+
// add indices across all PITs to a set and evaluate if user has access to all indices
79+
for(String[] indices: pitToIndicesMap.values()) {
80+
pitIndices.addAll(Arrays.asList(indices));
81+
}
82+
Set<String> allPermittedIndices = getPermittedIndices(pitIndices, clusterService, user,
83+
securityRoles, action, resolver, irr);
84+
// Only if user has access to all PIT's indices, allow operation, otherwise continue evaluation in PrivilegesEvaluator.
85+
if(allPermittedIndices.containsAll(pitIndices)) {
86+
presponse.allowed = true;
87+
presponse.markComplete();
88+
}
89+
return presponse;
90+
}
91+
92+
/**
93+
* This method returns list of permitted indices for the PIT indices passed
94+
*/
95+
private Set<String> getPermittedIndices(Set<String> pitIndices, ClusterService clusterService,
96+
User user, SecurityRoles securityRoles, final String action,
97+
IndexNameExpressionResolver resolver, final IndexResolverReplacer irr) {
98+
String[] indicesArr = new String[pitIndices.size()];
99+
CreatePitRequest req = new CreatePitRequest(new TimeValue(1, TimeUnit.DAYS), true,
100+
pitIndices.toArray(indicesArr));
101+
final IndexResolverReplacer.Resolved pitResolved = irr.resolveRequest(req);
102+
return securityRoles.reduce(pitResolved,
103+
user, new String[]{action}, resolver, clusterService);
104+
}
105+
}

src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ public class PrivilegesEvaluator {
130130
private final SecurityIndexAccessEvaluator securityIndexAccessEvaluator;
131131
private final ProtectedIndexAccessEvaluator protectedIndexAccessEvaluator;
132132
private final TermsAggregationEvaluator termsAggregationEvaluator;
133+
private final PitPrivilegesEvaluator pitPrivilegesEvaluator;
133134
private final boolean dlsFlsEnabled;
134135
private final boolean dfmEmptyOverwritesAll;
135136
private DynamicConfigModel dcm;
@@ -158,6 +159,7 @@ public PrivilegesEvaluator(final ClusterService clusterService, final ThreadPool
158159
securityIndexAccessEvaluator = new SecurityIndexAccessEvaluator(settings, auditLog, irr);
159160
protectedIndexAccessEvaluator = new ProtectedIndexAccessEvaluator(settings, auditLog);
160161
termsAggregationEvaluator = new TermsAggregationEvaluator();
162+
pitPrivilegesEvaluator = new PitPrivilegesEvaluator();
161163
this.namedXContentRegistry = namedXContentRegistry;
162164
this.dlsFlsEnabled = dlsFlsEnabled;
163165
this.dfmEmptyOverwritesAll = settings.getAsBoolean(ConfigConstants.SECURITY_DFM_EMPTY_OVERRIDES_ALL, false);
@@ -282,6 +284,12 @@ public PrivilegesEvaluatorResponse evaluate(final User user, String action0, fin
282284
return presponse;
283285
}
284286

287+
// check access for point in time requests
288+
if(pitPrivilegesEvaluator.evaluate(request, clusterService, user, securityRoles,
289+
action0, resolver, presponse, irr).isComplete()) {
290+
return presponse;
291+
}
292+
285293
final boolean dnfofEnabled = dcm.isDnfofEnabled();
286294

287295
final boolean isTraceEnabled = log.isTraceEnabled();

src/main/java/org/opensearch/security/resolver/IndexResolverReplacer.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,11 @@ public final static class Resolved {
370370
private final boolean isLocalAll;
371371
private final IndicesOptions indicesOptions;
372372

373-
private Resolved(final ImmutableSet<String> aliases,
374-
final ImmutableSet<String> allIndices,
375-
final ImmutableSet<String> originalRequested,
376-
final ImmutableSet<String> remoteIndices,
377-
IndicesOptions indicesOptions) {
373+
public Resolved(final ImmutableSet<String> aliases,
374+
final ImmutableSet<String> allIndices,
375+
final ImmutableSet<String> originalRequested,
376+
final ImmutableSet<String> remoteIndices,
377+
IndicesOptions indicesOptions) {
378378
this.aliases = aliases;
379379
this.allIndices = allIndices;
380380
this.originalRequested = originalRequested;

src/main/resources/static_config/static_action_groups.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,9 @@ manage_point_in_time:
233233
static: true
234234
allowed_actions:
235235
- "indices:data/read/point_in_time/create"
236-
- "cluster:admin/point_in_time/delete"
237-
- "cluster:admin/point_in_time/read*"
236+
- "indices:data/read/point_in_time/delete"
237+
- "indices:data/read/point_in_time/readall"
238+
- "indices:data/read/search"
238239
- "indices:monitor/point_in_time/segments"
239-
type: "cluster"
240+
type: "index"
240241
description: "Manage point in time actions"

0 commit comments

Comments
 (0)