Skip to content

Commit 57d4e15

Browse files
authored
Bugfix: Prevent invalid privileges in manage roles privilege (#128532)
This PR addresses the bug reported in [#127496](#127496) **Changes:** - Added validation logic in `ConfigurableClusterPrivileges` to ensure privileges defined for a global cluster manage role privilege are valid - Added unit test to `ManageRolePrivilegesTest` to ensure invalid privilege is caught during role creation - Updated `BulkPutRoleRestIT` to assert that an error is thrown and that the role is not created. Both existing and new unit/integration tests passed locally.
1 parent 68d5cc6 commit 57d4e15

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

docs/changelog/128532.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 128532
2+
summary: "Prevent invalid privileges in manage roles privilege"
3+
area: "Authorization"
4+
type: bug
5+
issues: [127496]

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,11 @@ public static ManageRolesPrivilege parse(XContentParser parser) throws IOExcepti
562562
}
563563
for (String privilege : indexPrivilege.privileges) {
564564
IndexPrivilege namedPrivilege = IndexPrivilege.getNamedOrNull(privilege);
565+
566+
// Use resolveBySelectorAccess to determine whether the passed privilege is valid.
567+
// IllegalArgumentException is thrown here when an invalid permission is encountered.
568+
IndexPrivilege.resolveBySelectorAccess(Set.of(privilege));
569+
565570
if (namedPrivilege != null && namedPrivilege.getSelectorPredicate() == IndexComponentSelectorPredicate.FAILURES) {
566571
throw new IllegalArgumentException(
567572
"Failure store related privileges are not supported as targets of manage roles but found [" + privilege + "]"

x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageRolesPrivilegesTests.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,13 @@
3434

3535
import java.io.ByteArrayOutputStream;
3636
import java.io.IOException;
37+
import java.nio.charset.StandardCharsets;
3738
import java.util.Arrays;
3839
import java.util.List;
40+
import java.util.Locale;
3941
import java.util.Objects;
4042

43+
import static org.hamcrest.Matchers.containsString;
4144
import static org.hamcrest.Matchers.nullValue;
4245
import static org.hamcrest.core.Is.is;
4346
import static org.hamcrest.core.IsEqual.equalTo;
@@ -257,6 +260,70 @@ public void testPutRoleRequestContainsNonIndexPrivileges() {
257260
assertThat(permissionCheck(permission, "cluster:admin/xpack/security/role/put", putRoleRequest), is(false));
258261
}
259262

263+
public void testParseInvalidPrivilege() throws Exception {
264+
final String unknownPrivilege = randomValueOtherThanMany(
265+
i -> IndexPrivilege.values().containsKey(i),
266+
() -> randomAlphaOfLength(10).toLowerCase(Locale.ROOT)
267+
);
268+
269+
final String invalidJsonString = String.format(Locale.ROOT, """
270+
{
271+
"manage": {
272+
"indices": [
273+
{
274+
"names": ["test-*"],
275+
"privileges": ["%s"]
276+
}
277+
]
278+
}
279+
}""", unknownPrivilege);
280+
assertInvalidPrivilegeParsing(invalidJsonString, unknownPrivilege);
281+
}
282+
283+
public void testParseMixedValidAndInvalidPrivileges() throws Exception {
284+
final String unknownPrivilege = randomValueOtherThanMany(
285+
i -> IndexPrivilege.values().containsKey(i),
286+
() -> randomAlphaOfLength(10).toLowerCase(Locale.ROOT)
287+
);
288+
289+
final String validPrivilege = "read";
290+
final String mixedPrivilegesJson = String.format(Locale.ROOT, """
291+
{
292+
"manage": {
293+
"indices": [
294+
{
295+
"names": ["test-*"],
296+
"privileges": ["%s", "%s"]
297+
}
298+
]
299+
}
300+
}""", validPrivilege, unknownPrivilege);
301+
302+
assertInvalidPrivilegeParsing(mixedPrivilegesJson, unknownPrivilege);
303+
}
304+
305+
/**
306+
* Helper method to assert that parsing the given JSON payload results in an
307+
* IllegalArgumentException due to an unknown privilege.
308+
*
309+
* @param jsonPayload The JSON string containing the privilege data.
310+
* @param expectedErrorDetail The specific unknown privilege name expected in the error message.
311+
*/
312+
private static void assertInvalidPrivilegeParsing(final String jsonPayload, final String expectedErrorDetail) throws Exception {
313+
final XContent xContent = XContentType.JSON.xContent();
314+
315+
try (
316+
XContentParser parser = xContent.createParser(XContentParserConfiguration.EMPTY, jsonPayload.getBytes(StandardCharsets.UTF_8))
317+
) {
318+
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
319+
assertThat(parser.nextToken(), equalTo(XContentParser.Token.FIELD_NAME));
320+
321+
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> ManageRolesPrivilege.parse(parser));
322+
323+
assertThat(exception.getMessage(), containsString("unknown index privilege [" + expectedErrorDetail + "]"));
324+
}
325+
}
326+
260327
private static boolean permissionCheck(ClusterPermission permission, String action, ActionRequest request) {
261328
final Authentication authentication = AuthenticationTestHelper.builder().build();
262329
assertThat(request.validate(), nullValue());
Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import static org.hamcrest.Matchers.hasSize;
2222
import static org.hamcrest.Matchers.not;
2323

24-
public class BulkPutRoleRestIT extends SecurityOnTrialLicenseRestTestCase {
24+
public class PutRoleRestIT extends SecurityOnTrialLicenseRestTestCase {
2525
public void testPutManyValidRoles() throws Exception {
2626
Map<String, Object> responseMap = upsertRoles("""
2727
{"roles": {"test1": {"cluster": ["all"],"indices": [{"names": ["*"],"privileges": ["all"]}]}, "test2":
@@ -312,4 +312,40 @@ public void testBulkUpdates() throws Exception {
312312
);
313313
}
314314
}
315+
316+
public void testPutRoleWithInvalidManageRolesPrivilege() throws Exception {
317+
final String badRoleName = "bad-role";
318+
319+
final ResponseException exception = expectThrows(ResponseException.class, () -> upsertRoles(String.format("""
320+
{
321+
"roles": {
322+
"%s": {
323+
"global": {
324+
"role": {
325+
"manage": {
326+
"indices": [
327+
{
328+
"names": ["allowed-index-prefix-*"],
329+
"privileges": ["foobar"]
330+
}
331+
]
332+
}
333+
}
334+
}
335+
}
336+
}
337+
}""", badRoleName)));
338+
339+
assertThat(exception.getMessage(), containsString("unknown index privilege [foobar]"));
340+
assertEquals(400, exception.getResponse().getStatusLine().getStatusCode());
341+
assertRoleDoesNotExist(badRoleName);
342+
}
343+
344+
private void assertRoleDoesNotExist(final String roleName) throws Exception {
345+
final ResponseException roleNotFound = expectThrows(
346+
ResponseException.class,
347+
() -> adminClient().performRequest(new Request("GET", "/_security/role/" + roleName))
348+
);
349+
assertEquals(404, roleNotFound.getResponse().getStatusLine().getStatusCode());
350+
}
315351
}

0 commit comments

Comments
 (0)