Skip to content

Commit be293d6

Browse files
committed
Support selectors
1 parent 132c6b0 commit be293d6

File tree

9 files changed

+198
-104
lines changed

9 files changed

+198
-104
lines changed

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ static TransportVersion def(int id) {
180180
public static final TransportVersion REMOVE_DESIRED_NODE_VERSION = def(9_004_0_00);
181181
public static final TransportVersion ESQL_DRIVER_TASK_DESCRIPTION = def(9_005_0_00);
182182
public static final TransportVersion ESQL_RETRY_ON_SHARD_LEVEL_FAILURE = def(9_006_0_00);
183+
public static final TransportVersion ROLE_SELECTORS_FIELD = def(9_007_0_00);
183184

184185
/*
185186
* STOP! READ THIS FIRST! No, really,

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@
5151
import java.util.Locale;
5252
import java.util.Map;
5353
import java.util.Objects;
54-
import java.util.Set;
55-
import java.util.stream.Collectors;
5654

5755
import static org.elasticsearch.common.xcontent.XContentHelper.createParserNotCompressed;
5856
import static org.elasticsearch.xpack.core.security.authz.permission.RemoteClusterPermissions.ROLE_REMOTE_CLUSTER_PRIVS;
@@ -925,6 +923,7 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
925923
String[] deniedFields = null;
926924
boolean allowRestrictedIndices = false;
927925
String[] remoteClusters = null;
926+
List<String> selectors = null;
928927
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
929928
if (token == XContentParser.Token.FIELD_NAME) {
930929
currentFieldName = parser.currentName();
@@ -1082,6 +1081,8 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
10821081
Fields.TRANSIENT_METADATA
10831082
);
10841083
}
1084+
} else if (Fields.SELECTORS.match(currentFieldName, parser.getDeprecationHandler())) {
1085+
selectors = Arrays.stream(readStringArray(roleName, parser, true)).toList();
10851086
} else if (allowRemoteClusters && Fields.CLUSTERS.match(currentFieldName, parser.getDeprecationHandler())) {
10861087
remoteClusters = readStringArray(roleName, parser, false);
10871088
} else {
@@ -1116,6 +1117,9 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
11161117
);
11171118
}
11181119
checkIfExceptFieldsIsSubsetOfGrantedFields(roleName, grantedFields, deniedFields);
1120+
if (selectors == null) {
1121+
selectors = IndicesPrivileges.DEFAULT_SELECTORS;
1122+
}
11191123
return new IndicesPrivilegesWithOptionalRemoteClusters(
11201124
IndicesPrivileges.builder()
11211125
.indices(names)
@@ -1124,6 +1128,7 @@ private static IndicesPrivilegesWithOptionalRemoteClusters parseIndexWithOptiona
11241128
.deniedFields(deniedFields)
11251129
.query(query)
11261130
.allowRestrictedIndices(allowRestrictedIndices)
1131+
.selectors(selectors)
11271132
.build(),
11281133
remoteClusters
11291134
);
@@ -1337,6 +1342,7 @@ public RemoteIndicesPrivileges build() {
13371342
*/
13381343
public static class IndicesPrivileges implements ToXContentObject, Writeable, Comparable<IndicesPrivileges> {
13391344

1345+
private static final List<String> DEFAULT_SELECTORS = List.of(IndexComponentSelector.DATA.getKey());
13401346
private static final IndicesPrivileges[] NONE = new IndicesPrivileges[0];
13411347

13421348
private String[] indices;
@@ -1348,6 +1354,7 @@ public static class IndicesPrivileges implements ToXContentObject, Writeable, Co
13481354
// users. Setting this flag eliminates this special status, and any index name pattern in the permission will cover restricted
13491355
// indices as well.
13501356
private boolean allowRestrictedIndices = false;
1357+
private List<String> selectors;
13511358

13521359
private IndicesPrivileges() {}
13531360

@@ -1358,6 +1365,11 @@ public IndicesPrivileges(StreamInput in) throws IOException {
13581365
this.privileges = in.readStringArray();
13591366
this.query = in.readOptionalBytesReference();
13601367
this.allowRestrictedIndices = in.readBoolean();
1368+
if (in.getTransportVersion().onOrAfter(TransportVersions.ROLE_SELECTORS_FIELD)) {
1369+
this.selectors = in.readStringCollectionAsList();
1370+
} else {
1371+
this.selectors = DEFAULT_SELECTORS;
1372+
}
13611373
}
13621374

13631375
@Override
@@ -1368,6 +1380,9 @@ public void writeTo(StreamOutput out) throws IOException {
13681380
out.writeStringArray(privileges);
13691381
out.writeOptionalBytesReference(query);
13701382
out.writeBoolean(allowRestrictedIndices);
1383+
if (out.getTransportVersion().onOrAfter(TransportVersions.ROLE_SELECTORS_FIELD)) {
1384+
out.writeStringCollection(selectors);
1385+
}
13711386
}
13721387

13731388
public static Builder builder() {
@@ -1378,11 +1393,12 @@ public String[] getIndices() {
13781393
return this.indices;
13791394
}
13801395

1381-
public static Set<String> filterFailureIndices(Set<String> indices) {
1382-
return indices.stream().filter(index -> {
1383-
// TODO improve check
1384-
return index != null && index.endsWith("::" + IndexComponentSelector.FAILURES.getKey());
1385-
}).collect(Collectors.toSet());
1396+
public boolean matchesSelector(IndexComponentSelector selector) {
1397+
return selectors.contains(selector.getKey());
1398+
}
1399+
1400+
public List<String> getSelectors() {
1401+
return this.selectors;
13861402
}
13871403

13881404
public String[] getPrivileges() {
@@ -1438,6 +1454,7 @@ public boolean hasGrantedFields() {
14381454

14391455
@Override
14401456
public String toString() {
1457+
// TODO selectors
14411458
StringBuilder sb = new StringBuilder("IndicesPrivileges[");
14421459
sb.append("indices=[").append(Strings.arrayToCommaDelimitedString(indices));
14431460
sb.append("], allowRestrictedIndices=[").append(allowRestrictedIndices);
@@ -1482,6 +1499,8 @@ public boolean equals(Object o) {
14821499
if (Arrays.equals(privileges, that.privileges) == false) return false;
14831500
if (Arrays.equals(grantedFields, that.grantedFields) == false) return false;
14841501
if (Arrays.equals(deniedFields, that.deniedFields) == false) return false;
1502+
// TODO
1503+
if (Objects.equals(selectors, that.selectors) == false) return false;
14851504
return Objects.equals(query, that.query);
14861505
}
14871506

@@ -1493,6 +1512,8 @@ public int hashCode() {
14931512
result = 31 * result + Arrays.hashCode(grantedFields);
14941513
result = 31 * result + Arrays.hashCode(deniedFields);
14951514
result = 31 * result + (query != null ? query.hashCode() : 0);
1515+
// TODO NPE?
1516+
result = 31 * result + (selectors != null ? selectors.hashCode() : 0);
14961517
return result;
14971518
}
14981519

@@ -1521,6 +1542,10 @@ XContentBuilder innerToXContent(XContentBuilder builder, boolean withPrivileges)
15211542
if (query != null) {
15221543
builder.field("query", query.utf8ToString());
15231544
}
1545+
// Hide default selectors
1546+
if (selectors.equals(List.of("data")) == false) {
1547+
builder.stringListField("selectors", selectors);
1548+
}
15241549
return builder.field(RoleDescriptor.Fields.ALLOW_RESTRICTED_INDICES.getPreferredName(), allowRestrictedIndices);
15251550
}
15261551

@@ -1577,6 +1602,11 @@ public Builder privileges(String... privileges) {
15771602
return this;
15781603
}
15791604

1605+
public Builder selectors(List<String> selectors) {
1606+
indicesPrivileges.selectors = selectors;
1607+
return this;
1608+
}
1609+
15801610
public Builder privileges(Collection<String> privileges) {
15811611
return privileges(privileges.toArray(new String[privileges.size()]));
15821612
}
@@ -1888,6 +1918,7 @@ public interface Fields {
18881918
ParseField APPLICATIONS = new ParseField("applications");
18891919
ParseField RUN_AS = new ParseField("run_as");
18901920
ParseField NAMES = new ParseField("names");
1921+
ParseField SELECTORS = new ParseField("selectors");
18911922
ParseField ALLOW_RESTRICTED_INDICES = new ParseField("allow_restricted_indices");
18921923
ParseField RESOURCES = new ParseField("resources");
18931924
ParseField QUERY = new ParseField("query");

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/permission/Role.java

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -305,18 +305,17 @@ public Builder add(
305305
IndexComponentSelector selector,
306306
String... indices
307307
) {
308-
// TODO assert no failures suffixes left
309308
groups.add(new IndicesPermissionGroupDefinition(privilege, fieldPermissions, query, allowRestrictedIndices, selector, indices));
310309
return this;
311310
}
312311

313-
// TODO allowFailureStoreAccess
314312
public Builder addRemoteIndicesGroup(
315313
final Set<String> remoteClusterAliases,
316314
final FieldPermissions fieldPermissions,
317315
final Set<BytesReference> query,
318316
final IndexPrivilege privilege,
319317
final boolean allowRestrictedIndices,
318+
IndexComponentSelector indexComponentSelector,
320319
final String... indices
321320
) {
322321
remoteIndicesGroups.computeIfAbsent(remoteClusterAliases, k -> new ArrayList<>())
@@ -326,7 +325,7 @@ public Builder addRemoteIndicesGroup(
326325
fieldPermissions,
327326
query,
328327
allowRestrictedIndices,
329-
IndexComponentSelector.DATA,
328+
indexComponentSelector,
330329
indices
331330
)
332331
);
@@ -472,28 +471,41 @@ static SimpleRole buildFromRoleDescriptor(
472471
indexPrivilege.getQuery() == null ? null : Collections.singleton(indexPrivilege.getQuery()),
473472
IndexPrivilege.get(Sets.newHashSet(indexPrivilege.getPrivileges())),
474473
indexPrivilege.allowRestrictedIndices(),
475-
IndexComponentSelector.ALL_APPLICABLE,
476-
indexPrivilege.getIndices()
477-
);
478-
} else {
479-
builder.add(
480-
fieldPermissionsCache.getFieldPermissions(
481-
new FieldPermissionsDefinition(indexPrivilege.getGrantedFields(), indexPrivilege.getDeniedFields())
482-
),
483-
indexPrivilege.getQuery() == null ? null : Collections.singleton(indexPrivilege.getQuery()),
484-
IndexPrivilege.get(Sets.newHashSet(indexPrivilege.getPrivileges())),
485-
indexPrivilege.allowRestrictedIndices(),
486-
IndexComponentSelector.DATA,
474+
IndexComponentSelector.FAILURES,
487475
indexPrivilege.getIndices()
488476
);
489477
}
478+
builder.add(
479+
fieldPermissionsCache.getFieldPermissions(
480+
new FieldPermissionsDefinition(indexPrivilege.getGrantedFields(), indexPrivilege.getDeniedFields())
481+
),
482+
indexPrivilege.getQuery() == null ? null : Collections.singleton(indexPrivilege.getQuery()),
483+
IndexPrivilege.get(Sets.newHashSet(indexPrivilege.getPrivileges())),
484+
indexPrivilege.allowRestrictedIndices(),
485+
IndexComponentSelector.DATA,
486+
indexPrivilege.getIndices()
487+
);
490488
}
491489

492490
for (RoleDescriptor.RemoteIndicesPrivileges remoteIndicesPrivileges : roleDescriptor.getRemoteIndicesPrivileges()) {
493491
final String[] clusterAliases = remoteIndicesPrivileges.remoteClusters();
494492
assert Arrays.equals(new String[] { "*" }, clusterAliases)
495493
: "reserved role should not define remote indices privileges for specific clusters";
496494
final RoleDescriptor.IndicesPrivileges indicesPrivileges = remoteIndicesPrivileges.indicesPrivileges();
495+
// TODO properly handle this
496+
if (Arrays.asList(indicesPrivileges.getIndices()).contains("*")) {
497+
builder.addRemoteIndicesGroup(
498+
Set.of(clusterAliases),
499+
fieldPermissionsCache.getFieldPermissions(
500+
new FieldPermissionsDefinition(indicesPrivileges.getGrantedFields(), indicesPrivileges.getDeniedFields())
501+
),
502+
indicesPrivileges.getQuery() == null ? null : Collections.singleton(indicesPrivileges.getQuery()),
503+
IndexPrivilege.get(Set.of(indicesPrivileges.getPrivileges())),
504+
indicesPrivileges.allowRestrictedIndices(),
505+
IndexComponentSelector.FAILURES,
506+
indicesPrivileges.getIndices()
507+
);
508+
}
497509
builder.addRemoteIndicesGroup(
498510
Set.of(clusterAliases),
499511
fieldPermissionsCache.getFieldPermissions(
@@ -502,6 +514,7 @@ static SimpleRole buildFromRoleDescriptor(
502514
indicesPrivileges.getQuery() == null ? null : Collections.singleton(indicesPrivileges.getQuery()),
503515
IndexPrivilege.get(Set.of(indicesPrivileges.getPrivileges())),
504516
indicesPrivileges.allowRestrictedIndices(),
517+
IndexComponentSelector.DATA,
505518
indicesPrivileges.getIndices()
506519
);
507520
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/LimitedRoleTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
102102
baseQuery,
103103
basePrivilege,
104104
baseAllowRestrictedIndices,
105+
IndexComponentSelector.DATA,
105106
baseIndices
106107
)
107108
// This privilege should be ignored (wrong alias)
@@ -111,6 +112,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
111112
randomDlsQuery(),
112113
randomIndexPrivilege(),
113114
randomBoolean(),
115+
IndexComponentSelector.DATA,
114116
randomAlphaOfLengthBetween(4, 6)
115117
)
116118
.addRemoteClusterPermissions(
@@ -147,6 +149,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
147149
limitedQuery,
148150
limitedPrivilege,
149151
limitedAllowRestrictedIndices,
152+
IndexComponentSelector.DATA,
150153
limitedIndices
151154
)
152155
// This privilege should be ignored (wrong alias)
@@ -156,6 +159,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
156159
randomDlsQuery(),
157160
randomIndexPrivilege(),
158161
randomBoolean(),
162+
IndexComponentSelector.DATA,
159163
randomAlphaOfLength(9)
160164
)
161165
.addRemoteClusterPermissions(
@@ -267,6 +271,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
267271
randomDlsQuery(),
268272
randomIndexPrivilege(),
269273
randomBoolean(),
274+
IndexComponentSelector.DATA,
270275
randomAlphaOfLength(3)
271276
);
272277
baseRole.addRemoteClusterPermissions(remoteCluster);
@@ -278,6 +283,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
278283
randomDlsQuery(),
279284
randomIndexPrivilege(),
280285
randomBoolean(),
286+
IndexComponentSelector.DATA,
281287
randomAlphaOfLength(4)
282288
);
283289
limitedByRole1.addRemoteClusterPermissions(remoteCluster);
@@ -289,6 +295,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
289295
randomDlsQuery(),
290296
randomIndexPrivilege(),
291297
randomBoolean(),
298+
IndexComponentSelector.DATA,
292299
randomAlphaOfLength(5)
293300
);
294301
limitedByRole2.addRemoteClusterPermissions(remoteCluster);
@@ -307,6 +314,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
307314
randomDlsQuery(),
308315
randomIndexPrivilege(),
309316
randomBoolean(),
317+
IndexComponentSelector.DATA,
310318
randomAlphaOfLength(5)
311319
);
312320
limitedByRole1.addRemoteIndicesGroup(
@@ -315,6 +323,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
315323
randomDlsQuery(),
316324
randomIndexPrivilege(),
317325
randomBoolean(),
326+
IndexComponentSelector.DATA,
318327
randomAlphaOfLength(4)
319328
);
320329
limitedByRole2.addRemoteIndicesGroup(
@@ -323,6 +332,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteClusterReturnsEmpty() {
323332
randomDlsQuery(),
324333
randomIndexPrivilege(),
325334
randomBoolean(),
335+
IndexComponentSelector.DATA,
326336
randomAlphaOfLength(3)
327337
);
328338
}

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/permission/SimpleRoleTests.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package org.elasticsearch.xpack.core.security.authz.permission;
99

1010
import org.elasticsearch.TransportVersion;
11+
import org.elasticsearch.action.support.IndexComponentSelector;
1112
import org.elasticsearch.common.settings.Settings;
1213
import org.elasticsearch.test.ESTestCase;
1314
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
@@ -157,17 +158,27 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
157158
null,
158159
IndexPrivilege.READ,
159160
true,
161+
IndexComponentSelector.DATA,
160162
"remote-index-a-1",
161163
"remote-index-a-2"
162164
)
163-
.addRemoteIndicesGroup(Set.of("remote-*-a"), FieldPermissions.DEFAULT, null, IndexPrivilege.READ, false, "remote-index-a-3")
165+
.addRemoteIndicesGroup(
166+
Set.of("remote-*-a"),
167+
FieldPermissions.DEFAULT,
168+
null,
169+
IndexPrivilege.READ,
170+
false,
171+
IndexComponentSelector.DATA,
172+
"remote-index-a-3"
173+
)
164174
// This privilege should be ignored (wrong alias)
165175
.addRemoteIndicesGroup(
166176
Set.of("remote-cluster-b"),
167177
FieldPermissions.DEFAULT,
168178
null,
169179
IndexPrivilege.READ,
170180
false,
181+
IndexComponentSelector.DATA,
171182
"remote-index-b-1",
172183
"remote-index-b-2"
173184
)
@@ -178,6 +189,7 @@ public void testGetRoleDescriptorsIntersectionForRemoteCluster() {
178189
null,
179190
IndexPrivilege.get(Set.of(randomFrom(IndexPrivilege.names()))),
180191
randomBoolean(),
192+
IndexComponentSelector.DATA,
181193
randomAlphaOfLength(9)
182194
)
183195
.addRemoteClusterPermissions(

x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/FailureStoreSecurityRestIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ public void testFailureStoreAccess() throws IOException {
4747
{
4848
"description": "Role with data access",
4949
"cluster": ["all"],
50-
"indices": [{"names": ["test*"], "privileges": ["read"]}]
50+
"indices": [{"names": ["test*"], "privileges": ["read"], "selectors": ["data"]}]
5151
}"""), dataAccessRole);
5252
upsertRole(Strings.format("""
5353
{
5454
"description": "Role with failure store access",
5555
"cluster": ["all"],
56-
"indices": [{"names": ["test*::failures"], "privileges": ["read"]}]
56+
"indices": [{"names": ["test*"], "privileges": ["read"], "selectors": ["failures"]}]
5757
}"""), failureStoreAccessRole);
5858

5959
createTemplates();

0 commit comments

Comments
 (0)