Skip to content

Commit 0e5024a

Browse files
filter reserved roles in other cases and add tests
1 parent db41a40 commit 0e5024a

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
package org.elasticsearch.integration;
8+
9+
import org.elasticsearch.action.support.PlainActionFuture;
10+
import org.elasticsearch.test.NativeRealmIntegTestCase;
11+
import org.elasticsearch.test.TestSecurityClient;
12+
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
13+
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
14+
import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult;
15+
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
16+
import org.elasticsearch.xpack.security.support.SecuritySystemIndices;
17+
import org.junit.Before;
18+
import org.junit.BeforeClass;
19+
20+
import java.io.IOException;
21+
import java.util.HashSet;
22+
import java.util.Set;
23+
24+
import static org.elasticsearch.test.SecuritySettingsSource.SECURITY_REQUEST_OPTIONS;
25+
import static org.hamcrest.Matchers.containsInAnyOrder;
26+
import static org.hamcrest.Matchers.empty;
27+
import static org.hamcrest.Matchers.is;
28+
import static org.hamcrest.Matchers.notNullValue;
29+
30+
/**
31+
* Test for the {@link NativeRolesStore#getRoleDescriptors} method.
32+
*/
33+
public class GeRoleDescriptorsTests extends NativeRealmIntegTestCase {
34+
35+
private static Set<String> customRoles;
36+
37+
@BeforeClass
38+
public static void init() throws Exception {
39+
new ReservedRolesStore();
40+
41+
final int numOfRoles = randomIntBetween(5, 10);
42+
customRoles = new HashSet<>(numOfRoles);
43+
for (int i = 0; i < numOfRoles; i++) {
44+
customRoles.add("custom_role_" + randomAlphaOfLength(10) + "_" + i);
45+
}
46+
}
47+
48+
@Before
49+
public void setup() throws IOException {
50+
final TestSecurityClient securityClient = new TestSecurityClient(getRestClient(), SECURITY_REQUEST_OPTIONS);
51+
for (String role : customRoles) {
52+
final RoleDescriptor descriptor = new RoleDescriptor(
53+
role,
54+
new String[0],
55+
new RoleDescriptor.IndicesPrivileges[] {
56+
RoleDescriptor.IndicesPrivileges.builder()
57+
.indices("*")
58+
.privileges("ALL")
59+
.allowRestrictedIndices(randomBoolean())
60+
.build() },
61+
new String[0]
62+
);
63+
securityClient.putRole(descriptor);
64+
logger.info("--> created role [{}]", role);
65+
}
66+
67+
ensureGreen(SecuritySystemIndices.SECURITY_MAIN_ALIAS);
68+
}
69+
70+
public void testGetCustomRoles() {
71+
for (NativeRolesStore rolesStore : internalCluster().getInstances(NativeRolesStore.class)) {
72+
PlainActionFuture<RoleRetrievalResult> future = new PlainActionFuture<>();
73+
rolesStore.getRoleDescriptors(customRoles, future);
74+
RoleRetrievalResult result = future.actionGet();
75+
assertThat(result, notNullValue());
76+
assertTrue(result.isSuccess());
77+
assertThat(result.getDescriptors().stream().map(RoleDescriptor::getName).toList(), containsInAnyOrder(customRoles.toArray()));
78+
}
79+
}
80+
81+
public void testGetReservedRoles() {
82+
for (NativeRolesStore rolesStore : internalCluster().getInstances(NativeRolesStore.class)) {
83+
PlainActionFuture<RoleRetrievalResult> future = new PlainActionFuture<>();
84+
Set<String> reservedRoles = randomUnique(() -> randomFrom(ReservedRolesStore.names()), randomIntBetween(1, 5));
85+
rolesStore.getRoleDescriptors(reservedRoles, future);
86+
RoleRetrievalResult result = future.actionGet();
87+
assertThat(result, notNullValue());
88+
assertTrue(result.isSuccess());
89+
assertThat(result.getDescriptors(), is(empty()));
90+
}
91+
}
92+
93+
public void testGetAllRoles() {
94+
for (NativeRolesStore rolesStore : internalCluster().getInstances(NativeRolesStore.class)) {
95+
PlainActionFuture<RoleRetrievalResult> future = new PlainActionFuture<>();
96+
rolesStore.getRoleDescriptors(randomBoolean() ? null : Set.of(), future);
97+
RoleRetrievalResult result = future.actionGet();
98+
assertThat(result, notNullValue());
99+
assertTrue(result.isSuccess());
100+
assertThat(result.getDescriptors().stream().map(RoleDescriptor::getName).toList(), containsInAnyOrder(customRoles.toArray()));
101+
}
102+
}
103+
}

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

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ public void accept(Set<String> names, ActionListener<RoleRetrievalResult> listen
177177
getRoleDescriptors(names, listener);
178178
}
179179

180+
private Set<String> filterReservedRoles(Set<String> names) {
181+
if (names == null || names.isEmpty()) {
182+
return names;
183+
}
184+
Set<String> filtered = new HashSet<>(names.size());
185+
for (String name : names) {
186+
if (false == reservedRoleNameChecker.isReserved(name)) {
187+
filtered.add(name);
188+
}
189+
}
190+
return filtered;
191+
}
192+
180193
/**
181194
* Retrieve a list of roles, if rolesToGet is null or empty, fetch all roles
182195
*/
@@ -186,13 +199,21 @@ public void getRoleDescriptors(Set<String> names, final ActionListener<RoleRetri
186199
return;
187200
}
188201

202+
final Set<String> rolesToGet = filterReservedRoles(names);
203+
if ((names != null && names.size() > 0) && (rolesToGet == null || rolesToGet.isEmpty())) {
204+
// if we have no roles to get, it means all requested roles were reserved.
205+
// we can short-circuit and return an empty set here.
206+
listener.onResponse(RoleRetrievalResult.success(Set.of()));
207+
return;
208+
}
209+
189210
final SecurityIndexManager frozenSecurityIndex = this.securityIndex.defensiveCopy();
190211
if (frozenSecurityIndex.indexExists() == false) {
191212
// TODO remove this short circuiting and fix tests that fail without this!
192213
listener.onResponse(RoleRetrievalResult.success(Collections.emptySet()));
193214
} else if (frozenSecurityIndex.isAvailable(SEARCH_SHARDS) == false) {
194215
listener.onResponse(RoleRetrievalResult.failure(frozenSecurityIndex.getUnavailableReason(SEARCH_SHARDS)));
195-
} else if (names == null || names.isEmpty()) {
216+
} else if (rolesToGet == null || rolesToGet.isEmpty()) {
196217
securityIndex.checkIndexVersionThenExecute(listener::onFailure, () -> {
197218
QueryBuilder query = QueryBuilders.boolQuery()
198219
.must(QueryBuilders.termQuery(RoleDescriptor.Fields.TYPE.getPreferredName(), ROLE_TYPE))
@@ -220,11 +241,11 @@ public void getRoleDescriptors(Set<String> names, final ActionListener<RoleRetri
220241
);
221242
}
222243
});
223-
} else if (names.size() == 1) {
224-
getRoleDescriptor(Objects.requireNonNull(names.iterator().next()), listener);
244+
} else if (rolesToGet.size() == 1) {
245+
getRoleDescriptor(Objects.requireNonNull(rolesToGet.iterator().next()), listener);
225246
} else {
226247
securityIndex.checkIndexVersionThenExecute(listener::onFailure, () -> {
227-
final String[] roleIds = names.stream().map(NativeRolesStore::getIdForRole).toArray(String[]::new);
248+
final String[] roleIds = rolesToGet.stream().map(NativeRolesStore::getIdForRole).toArray(String[]::new);
228249
MultiGetRequest multiGetRequest = client.prepareMultiGet().addIds(SECURITY_MAIN_ALIAS, roleIds).request();
229250
executeAsyncWithOrigin(
230251
client.threadPool().getThreadContext(),

0 commit comments

Comments
 (0)