Skip to content

Commit b78a4d4

Browse files
committed
Support read_failures
1 parent 2f49f05 commit b78a4d4

File tree

5 files changed

+84
-66
lines changed

5 files changed

+84
-66
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ public final class IndexPrivilege extends Privilege {
179179
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
180180
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
181181
public static final IndexPrivilege READ = new IndexPrivilege("read", READ_AUTOMATON);
182+
// same automaton as read but SPECIAL
183+
public static final IndexPrivilege READ_FAILURES = new IndexPrivilege("read_failures", READ_AUTOMATON);
182184
public static final IndexPrivilege READ_CROSS_CLUSTER = new IndexPrivilege("read_cross_cluster", READ_CROSS_CLUSTER_AUTOMATON);
183185
public static final IndexPrivilege CREATE = new IndexPrivilege("create", CREATE_AUTOMATON);
184186
public static final IndexPrivilege INDEX = new IndexPrivilege("index", INDEX_AUTOMATON);
@@ -221,6 +223,7 @@ public final class IndexPrivilege extends Privilege {
221223
entry("create_index", CREATE_INDEX),
222224
entry("monitor", MONITOR),
223225
entry("read", READ),
226+
entry("read_failures", READ_FAILURES),
224227
entry("index", INDEX),
225228
entry("delete", DELETE),
226229
entry("write", WRITE),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public class ReservedRolesStore implements BiConsumer<Set<String>, ActionListene
7878
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("all").allowRestrictedIndices(false).build(),
7979
RoleDescriptor.IndicesPrivileges.builder()
8080
.indices("*")
81-
.privileges("monitor", "read", "view_index_metadata", "read_cross_cluster")
81+
.privileges("monitor", "read", "view_index_metadata", "read_cross_cluster", "read_failures")
8282
.allowRestrictedIndices(true)
8383
.build() },
8484
new RoleDescriptor.ApplicationResourcePrivileges[] {

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/InternalUsers.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ public class InternalUsers {
161161
IndicesStatsAction.NAME + "*",
162162
TransportUpdateSettingsAction.TYPE.name(),
163163
DownsampleAction.NAME,
164-
TransportAddIndexBlockAction.TYPE.name()
164+
TransportAddIndexBlockAction.TYPE.name(),
165+
"read_failures"
165166
)
166167
.allowRestrictedIndices(false)
167168
.build(),
@@ -180,7 +181,8 @@ public class InternalUsers {
180181
IndicesStatsAction.NAME + "*",
181182
TransportUpdateSettingsAction.TYPE.name(),
182183
DownsampleAction.NAME,
183-
TransportAddIndexBlockAction.TYPE.name()
184+
TransportAddIndexBlockAction.TYPE.name(),
185+
"read_failures"
184186
)
185187
.allowRestrictedIndices(true)
186188
.build() },
@@ -219,7 +221,8 @@ public class InternalUsers {
219221
TransportBulkAction.NAME,
220222
TransportIndexAction.NAME,
221223
TransportSearchScrollAction.TYPE.name(),
222-
ModifyDataStreamsAction.NAME
224+
ModifyDataStreamsAction.NAME,
225+
"read_failures"
223226
)
224227
.allowRestrictedIndices(false)
225228
.build() },
@@ -243,7 +246,7 @@ public class InternalUsers {
243246
new RoleDescriptor.IndicesPrivileges[] {
244247
RoleDescriptor.IndicesPrivileges.builder()
245248
.indices("*")
246-
.privileges(LazyRolloverAction.NAME)
249+
.privileges(LazyRolloverAction.NAME, "read_failures")
247250
.allowRestrictedIndices(true)
248251
.build() },
249252
null,

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

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,37 @@ public class FailureStoreSecurityRestIT extends SecurityOnTrialLicenseRestTestCa
3333

3434
private static final String DATA_ACCESS_USER = "data_access_user";
3535
private static final String FAILURE_STORE_ACCESS_USER = "failure_store_access_user";
36+
private static final String BOTH_ACCESS_USER = "both_access_user";
3637
private static final SecureString PASSWORD = new SecureString("elastic-password");
3738

3839
@SuppressWarnings("unchecked")
3940
public void testFailureStoreAccess() throws IOException {
4041
String dataAccessRole = "data_access";
4142
String failureStoreAccessRole = "failure_store_access";
43+
String bothAccessRole = "both_access";
4244

4345
createUser(DATA_ACCESS_USER, PASSWORD, List.of(dataAccessRole));
4446
createUser(FAILURE_STORE_ACCESS_USER, PASSWORD, List.of(failureStoreAccessRole));
47+
createUser(BOTH_ACCESS_USER, PASSWORD, List.of(bothAccessRole));
4548

4649
upsertRole(Strings.format("""
4750
{
4851
"description": "Role with data access",
4952
"cluster": ["all"],
50-
"indices": [{"names": ["test*"], "privileges": ["read"], "selectors": ["data"]}]
53+
"indices": [{"names": ["test*"], "privileges": ["read"]}]
5154
}"""), dataAccessRole);
5255
upsertRole(Strings.format("""
5356
{
5457
"description": "Role with failure store access",
5558
"cluster": ["all"],
56-
"indices": [{"names": ["test*"], "privileges": ["read"], "selectors": ["failures"]}]
59+
"indices": [{"names": ["test*"], "privileges": ["read_failures"]}]
5760
}"""), failureStoreAccessRole);
61+
upsertRole(Strings.format("""
62+
{
63+
"description": "Role with failure store access",
64+
"cluster": ["all"],
65+
"indices": [{"names": ["test*"], "privileges": ["read", "read_failures"]}]
66+
}"""), bothAccessRole);
5867

5968
createTemplates();
6069
List<String> docIds = populateDataStreamWithBulkRequest();
@@ -148,6 +157,23 @@ public void testFailureStoreAccess() throws IOException {
148157

149158
expectThrows404(() -> adminClient().performRequest(new Request("GET", "/test12::failures/_search")));
150159
expectThrows404(() -> adminClient().performRequest(new Request("GET", "/test2::failures/_search")));
160+
161+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/test1::failures/_search")), failedDocId);
162+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/test*::failures/_search")), failedDocId);
163+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/*1::failures/_search")), failedDocId);
164+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/*::failures/_search")), failedDocId);
165+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/.fs*/_search")), failedDocId);
166+
167+
expectThrows404(() -> performRequest(BOTH_ACCESS_USER, new Request("GET", "/test12::failures/_search")));
168+
expectThrows404(() -> performRequest(BOTH_ACCESS_USER, new Request("GET", "/test2::failures/_search")));
169+
170+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/test1/_search")), successDocId);
171+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/test*/_search")), successDocId);
172+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/*1/_search")), successDocId);
173+
assertContainsDocIds(performRequest(BOTH_ACCESS_USER, new Request("GET", "/*/_search")), successDocId);
174+
175+
expectThrows404(() -> performRequest(BOTH_ACCESS_USER, new Request("GET", "/test12/_search")));
176+
expectThrows404(() -> performRequest(BOTH_ACCESS_USER, new Request("GET", "/test2/_search")));
151177
}
152178

153179
private static void expectThrows404(ThrowingRunnable get) {

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java

Lines changed: 45 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,8 @@ public static void buildRoleFromDescriptors(
483483
final List<ConfigurableClusterPrivilege> configurableClusterPrivileges = new ArrayList<>();
484484
final Set<String> runAs = new HashSet<>();
485485

486-
final Map<Set<String>, MergeableIndicesPrivilege> indicesPrivilegesDataMap = new HashMap<>();
487-
final Map<Set<String>, MergeableIndicesPrivilege> indicesPrivilegesFailuresMap = new HashMap<>();
488-
final Map<Set<String>, MergeableIndicesPrivilege> restrictedIndicesPrivilegesDataMap = new HashMap<>();
489-
final Map<Set<String>, MergeableIndicesPrivilege> restrictedIndicesPrivilegesFailuresMap = new HashMap<>();
486+
final Map<Set<String>, MergeableIndicesPrivilege> indicesPrivilegesMap = new HashMap<>();
487+
final Map<Set<String>, MergeableIndicesPrivilege> restrictedIndicesPrivilegesMap = new HashMap<>();
490488

491489
final Map<Set<String>, Set<IndicesPrivileges>> remoteIndicesPrivilegesByCluster = new HashMap<>();
492490

@@ -507,30 +505,8 @@ public static void buildRoleFromDescriptors(
507505
runAs.addAll(Arrays.asList(descriptor.getRunAs()));
508506
}
509507

510-
MergeableIndicesPrivilege.collatePrivilegesByIndices(
511-
descriptor.getIndicesPrivileges(),
512-
true,
513-
IndexComponentSelector.DATA,
514-
restrictedIndicesPrivilegesDataMap
515-
);
516-
MergeableIndicesPrivilege.collatePrivilegesByIndices(
517-
descriptor.getIndicesPrivileges(),
518-
false,
519-
IndexComponentSelector.DATA,
520-
indicesPrivilegesDataMap
521-
);
522-
MergeableIndicesPrivilege.collatePrivilegesByIndices(
523-
descriptor.getIndicesPrivileges(),
524-
true,
525-
IndexComponentSelector.FAILURES,
526-
restrictedIndicesPrivilegesFailuresMap
527-
);
528-
MergeableIndicesPrivilege.collatePrivilegesByIndices(
529-
descriptor.getIndicesPrivileges(),
530-
false,
531-
IndexComponentSelector.FAILURES,
532-
indicesPrivilegesFailuresMap
533-
);
508+
MergeableIndicesPrivilege.collatePrivilegesByIndices(descriptor.getIndicesPrivileges(), true, restrictedIndicesPrivilegesMap);
509+
MergeableIndicesPrivilege.collatePrivilegesByIndices(descriptor.getIndicesPrivileges(), false, indicesPrivilegesMap);
534510

535511
if (descriptor.hasRemoteIndicesPrivileges()) {
536512
groupIndexPrivilegesByCluster(descriptor.getRemoteIndicesPrivileges(), remoteIndicesPrivilegesByCluster);
@@ -563,47 +539,57 @@ public static void buildRoleFromDescriptors(
563539
final Role.Builder builder = Role.builder(restrictedIndices, roleNames.toArray(Strings.EMPTY_ARRAY))
564540
.cluster(clusterPrivileges, configurableClusterPrivileges)
565541
.runAs(runAsPrivilege);
566-
indicesPrivilegesDataMap.forEach((key, privilege) -> {
567-
builder.add(
568-
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
569-
privilege.query,
570-
IndexPrivilege.get(privilege.privileges),
571-
false,
572-
IndexComponentSelector.DATA,
573-
privilege.indices.toArray(Strings.EMPTY_ARRAY)
574-
);
575542

576-
});
577-
restrictedIndicesPrivilegesDataMap.forEach((key, privilege) -> {
578-
// For a privilege with both failure and non-failure indices, we need to split them into two separate groups
579-
builder.add(
580-
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
581-
privilege.query,
582-
IndexPrivilege.get(privilege.privileges),
583-
true,
584-
IndexComponentSelector.DATA,
585-
privilege.indices.toArray(Strings.EMPTY_ARRAY)
586-
);
587-
});
588-
indicesPrivilegesFailuresMap.forEach((key, privilege) -> {
543+
indicesPrivilegesMap.forEach((key, privilege) -> {
544+
if (privilege.privileges.contains("read_failures")) {
545+
builder.add(
546+
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
547+
privilege.query,
548+
IndexPrivilege.get(Set.of("read_failures")),
549+
false,
550+
IndexComponentSelector.FAILURES,
551+
privilege.indices.toArray(Strings.EMPTY_ARRAY)
552+
);
553+
}
554+
Set<String> privilegesWithoutReadFailures = filterOutReadFailures(privilege.privileges);
555+
if (privilegesWithoutReadFailures.isEmpty()) {
556+
return;
557+
}
589558
builder.add(
590559
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
591560
privilege.query,
592-
IndexPrivilege.get(privilege.privileges),
561+
IndexPrivilege.get(privilegesWithoutReadFailures),
593562
false,
594-
IndexComponentSelector.FAILURES,
563+
(privilege.privileges.contains("all") || privilege.privileges.contains("ALL"))
564+
? IndexComponentSelector.ALL_APPLICABLE
565+
: IndexComponentSelector.DATA,
595566
privilege.indices.toArray(Strings.EMPTY_ARRAY)
596567
);
597568

598569
});
599-
restrictedIndicesPrivilegesFailuresMap.forEach((key, privilege) -> {
600-
// For a privilege with both failure and non-failure indices, we need to split them into two separate groups
570+
restrictedIndicesPrivilegesMap.forEach((key, privilege) -> {
571+
if (privilege.privileges.contains("read_failures")) {
572+
builder.add(
573+
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
574+
privilege.query,
575+
IndexPrivilege.get(Set.of("read_failures")),
576+
true,
577+
IndexComponentSelector.FAILURES,
578+
privilege.indices.toArray(Strings.EMPTY_ARRAY)
579+
);
580+
}
581+
Set<String> privilegesWithoutReadFailures = filterOutReadFailures(privilege.privileges);
582+
if (privilegesWithoutReadFailures.isEmpty()) {
583+
return;
584+
}
601585
builder.add(
602586
fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition),
603587
privilege.query,
604588
IndexPrivilege.get(privilege.privileges),
605589
true,
606-
IndexComponentSelector.FAILURES,
590+
(privilege.privileges.contains("all") || privilege.privileges.contains("ALL"))
591+
? IndexComponentSelector.ALL_APPLICABLE
592+
: IndexComponentSelector.DATA,
607593
privilege.indices.toArray(Strings.EMPTY_ARRAY)
608594
);
609595
});
@@ -656,6 +642,10 @@ public static void buildRoleFromDescriptors(
656642
}
657643
}
658644

645+
private static Set<String> filterOutReadFailures(Set<String> privileges) {
646+
return privileges.stream().filter(p -> p.equals("read_failures") == false).collect(Collectors.toSet());
647+
}
648+
659649
public void invalidateAll() {
660650
numInvalidation.incrementAndGet();
661651
negativeLookupCache.invalidateAll();
@@ -766,7 +756,6 @@ void merge(MergeableIndicesPrivilege other) {
766756
private static void collatePrivilegesByIndices(
767757
final IndicesPrivileges[] indicesPrivileges,
768758
final boolean allowsRestrictedIndices,
769-
final IndexComponentSelector selector,
770759
final Map<Set<String>, MergeableIndicesPrivilege> indicesPrivilegesMap
771760
) {
772761
// if an index privilege is an explicit denial, then we treat it as non-existent since we skipped these in the past when
@@ -780,9 +769,6 @@ private static void collatePrivilegesByIndices(
780769
if (indicesPrivilege.allowRestrictedIndices() != allowsRestrictedIndices) {
781770
continue;
782771
}
783-
if (false == indicesPrivilege.matchesSelector(selector)) {
784-
continue;
785-
}
786772
final Set<String> key = newHashSet(indicesPrivilege.getIndices());
787773
indicesPrivilegesMap.compute(key, (k, value) -> {
788774
if (value == null) {

0 commit comments

Comments
 (0)