diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/ApiKey.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/ApiKey.java index 0120ed09e8fe4..57cf816a46072 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/ApiKey.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/ApiKey.java @@ -395,6 +395,7 @@ public String toString() { + "]"; } + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); static final ConstructingObjectParser PARSER; static { PARSER = new ConstructingObjectParser<>("api_key", true, ApiKey::new); @@ -419,7 +420,7 @@ static int initializeParser(AbstractObjectParser parser) { parser.declareObject(optionalConstructorArg(), (p, c) -> p.map(), new ParseField("metadata")); parser.declareNamedObjects(optionalConstructorArg(), (p, c, n) -> { p.nextToken(); - return RoleDescriptor.parse(n, p, false); + return ROLE_DESCRIPTOR_PARSER.parse(n, p); }, new ParseField("role_descriptors")); parser.declareField( optionalConstructorArg(), diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/BulkUpdateApiKeyRequestTranslator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/BulkUpdateApiKeyRequestTranslator.java index 57a5848970b2e..d4fdb2d7f1028 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/BulkUpdateApiKeyRequestTranslator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/BulkUpdateApiKeyRequestTranslator.java @@ -26,8 +26,9 @@ public interface BulkUpdateApiKeyRequestTranslator { BulkUpdateApiKeyRequest translate(RestRequest request) throws IOException; class Default implements BulkUpdateApiKeyRequestTranslator { + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); private static final ConstructingObjectParser PARSER = createParser( - (n, p) -> RoleDescriptor.parse(n, p, false) + (n, p) -> ROLE_DESCRIPTOR_PARSER.parse(n, p) ); @SuppressWarnings("unchecked") diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/CreateApiKeyRequestBuilder.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/CreateApiKeyRequestBuilder.java index a79b3c74db006..b8c4ab326fd34 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/CreateApiKeyRequestBuilder.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/CreateApiKeyRequestBuilder.java @@ -31,9 +31,8 @@ * Request builder for populating a {@link CreateApiKeyRequest} */ public class CreateApiKeyRequestBuilder extends ActionRequestBuilder { - private static final ConstructingObjectParser PARSER = createParser( - (n, p) -> RoleDescriptor.parse(n, p, false) - ); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); + private static final ConstructingObjectParser PARSER = createParser(ROLE_DESCRIPTOR_PARSER::parse); @SuppressWarnings("unchecked") public static ConstructingObjectParser createParser( diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/UpdateApiKeyRequestTranslator.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/UpdateApiKeyRequestTranslator.java index f70732dd50990..fa157224c79be 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/UpdateApiKeyRequestTranslator.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/apikey/UpdateApiKeyRequestTranslator.java @@ -25,7 +25,8 @@ public interface UpdateApiKeyRequestTranslator { UpdateApiKeyRequest translate(RestRequest request) throws IOException; class Default implements UpdateApiKeyRequestTranslator { - private static final ConstructingObjectParser PARSER = createParser((n, p) -> RoleDescriptor.parse(n, p, false)); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); + private static final ConstructingObjectParser PARSER = createParser(ROLE_DESCRIPTOR_PARSER::parse); @SuppressWarnings("unchecked") protected static ConstructingObjectParser createParser( diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/role/PutRoleRequestBuilder.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/role/PutRoleRequestBuilder.java index f389a39df7979..e2da04bb61534 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/role/PutRoleRequestBuilder.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/role/PutRoleRequestBuilder.java @@ -21,6 +21,8 @@ */ public class PutRoleRequestBuilder extends ActionRequestBuilder { + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().build(); + public PutRoleRequestBuilder(ElasticsearchClient client) { super(client, PutRoleAction.INSTANCE, new PutRoleRequest()); } @@ -29,9 +31,8 @@ public PutRoleRequestBuilder(ElasticsearchClient client) { * Populate the put role request from the source and the role's name */ public PutRoleRequestBuilder source(String name, BytesReference source, XContentType xContentType) throws IOException { - // we pass false as last parameter because we want to reject the request if field permissions - // are given in 2.x syntax - RoleDescriptor descriptor = RoleDescriptor.parse(name, source, false, xContentType, false); + // we want to reject the request if field permissions are given in 2.x syntax, hence we do not allow2xFormat + RoleDescriptor descriptor = ROLE_DESCRIPTOR_PARSER.parse(name, source, xContentType); assert name.equals(descriptor.getName()); request.name(name); request.cluster(descriptor.getClusterPrivileges()); diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/CrossClusterAccessSubjectInfo.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/CrossClusterAccessSubjectInfo.java index b6b54846fc7c4..f91df320bb92d 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/CrossClusterAccessSubjectInfo.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/CrossClusterAccessSubjectInfo.java @@ -223,6 +223,9 @@ private void validate() { public static final class RoleDescriptorsBytes implements Writeable { public static final RoleDescriptorsBytes EMPTY = new RoleDescriptorsBytes(new BytesArray("{}")); + + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().build(); + private final BytesReference rawBytes; public RoleDescriptorsBytes(BytesReference rawBytes) { @@ -263,7 +266,7 @@ public Set toRoleDescriptors() { while (parser.nextToken() != XContentParser.Token.END_OBJECT) { parser.nextToken(); final String roleName = parser.currentName(); - roleDescriptors.add(RoleDescriptor.parse(roleName, parser, false)); + roleDescriptors.add(ROLE_DESCRIPTOR_PARSER.parse(roleName, parser)); } return Set.copyOf(roleDescriptors); } catch (IOException e) { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java index ecbd12a7f4643..d1d24e2e4461e 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptor.java @@ -420,80 +420,112 @@ public void writeTo(StreamOutput out) throws IOException { } } - public static RoleDescriptor parse(String name, BytesReference source, boolean allow2xFormat, XContentType xContentType) - throws IOException { - return parse(name, source, allow2xFormat, xContentType, true); + public static Parser.Builder parserBuilder() { + return new Parser.Builder(); } - public static RoleDescriptor parse( - String name, - BytesReference source, - boolean allow2xFormat, - XContentType xContentType, - boolean allowRestriction - ) throws IOException { - assert name != null; - try (XContentParser parser = createParser(source, xContentType)) { - return parse(name, parser, allow2xFormat, allowRestriction); - } - } + public record Parser(boolean allow2xFormat, boolean allowRestriction) { - public static RoleDescriptor parse(String name, XContentParser parser, boolean allow2xFormat) throws IOException { - return parse(name, parser, allow2xFormat, true); - } + public static final class Builder { + private boolean allow2xFormat = false; + private boolean allowRestriction = false; + + private Builder() {} + + public Builder allow2xFormat(boolean allow2xFormat) { + this.allow2xFormat = allow2xFormat; + return this; + } + + public Builder allowRestriction(boolean allowRestriction) { + this.allowRestriction = allowRestriction; + return this; + } + + public Parser build() { + return new Parser(allow2xFormat, allowRestriction); + } - public static RoleDescriptor parse(String name, XContentParser parser, boolean allow2xFormat, boolean allowRestriction) - throws IOException { - // validate name - Validation.Error validationError = Validation.Roles.validateRoleName(name, true); - if (validationError != null) { - ValidationException ve = new ValidationException(); - ve.addValidationError(validationError.toString()); - throw ve; } - // advance to the START_OBJECT token if needed - XContentParser.Token token = parser.currentToken() == null ? parser.nextToken() : parser.currentToken(); - if (token != XContentParser.Token.START_OBJECT) { - throw new ElasticsearchParseException("failed to parse role [{}]. expected an object but found [{}] instead", name, token); + public RoleDescriptor parse(String name, BytesReference source, XContentType xContentType) throws IOException { + assert name != null; + try ( + XContentParser parser = XContentHelper.createParserNotCompressed( + LoggingDeprecationHandler.XCONTENT_PARSER_CONFIG, + source, + xContentType + ) + ) { + return parse(name, parser); + } } - String currentFieldName = null; - IndicesPrivileges[] indicesPrivileges = null; - RemoteIndicesPrivileges[] remoteIndicesPrivileges = null; - String[] clusterPrivileges = null; - List configurableClusterPrivileges = Collections.emptyList(); - ApplicationResourcePrivileges[] applicationPrivileges = null; - String[] runAsUsers = null; - Restriction restriction = null; - Map metadata = null; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (Fields.INDEX.match(currentFieldName, parser.getDeprecationHandler()) - || Fields.INDICES.match(currentFieldName, parser.getDeprecationHandler())) { - indicesPrivileges = parseIndices(name, parser, allow2xFormat); - } else if (Fields.RUN_AS.match(currentFieldName, parser.getDeprecationHandler())) { - runAsUsers = readStringArray(name, parser, true); - } else if (Fields.CLUSTER.match(currentFieldName, parser.getDeprecationHandler())) { - clusterPrivileges = readStringArray(name, parser, true); - } else if (Fields.APPLICATIONS.match(currentFieldName, parser.getDeprecationHandler()) - || Fields.APPLICATION.match(currentFieldName, parser.getDeprecationHandler())) { - applicationPrivileges = parseApplicationPrivileges(name, parser); - } else if (Fields.GLOBAL.match(currentFieldName, parser.getDeprecationHandler())) { - configurableClusterPrivileges = ConfigurableClusterPrivileges.parse(parser); - } else if (Fields.METADATA.match(currentFieldName, parser.getDeprecationHandler())) { - if (token != XContentParser.Token.START_OBJECT) { - throw new ElasticsearchParseException( - "expected field [{}] to be of type object, but found [{}] instead", - currentFieldName, - token - ); - } - metadata = parser.map(); - } else if (Fields.TRANSIENT_METADATA.match(currentFieldName, parser.getDeprecationHandler())) { - if (token == XContentParser.Token.START_OBJECT) { - // consume object but just drop - parser.map(); + + public RoleDescriptor parse(String name, XContentParser parser) throws IOException { + // validate name + Validation.Error validationError = Validation.Roles.validateRoleName(name, true); + if (validationError != null) { + ValidationException ve = new ValidationException(); + ve.addValidationError(validationError.toString()); + throw ve; + } + + // advance to the START_OBJECT token if needed + XContentParser.Token token = parser.currentToken() == null ? parser.nextToken() : parser.currentToken(); + if (token != XContentParser.Token.START_OBJECT) { + throw new ElasticsearchParseException("failed to parse role [{}]. expected an object but found [{}] instead", name, token); + } + String currentFieldName = null; + IndicesPrivileges[] indicesPrivileges = null; + RemoteIndicesPrivileges[] remoteIndicesPrivileges = null; + String[] clusterPrivileges = null; + List configurableClusterPrivileges = Collections.emptyList(); + ApplicationResourcePrivileges[] applicationPrivileges = null; + String[] runAsUsers = null; + Restriction restriction = null; + Map metadata = null; + String description = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (Fields.INDEX.match(currentFieldName, parser.getDeprecationHandler()) + || Fields.INDICES.match(currentFieldName, parser.getDeprecationHandler())) { + indicesPrivileges = parseIndices(name, parser, allow2xFormat); + } else if (Fields.RUN_AS.match(currentFieldName, parser.getDeprecationHandler())) { + runAsUsers = readStringArray(name, parser, true); + } else if (Fields.CLUSTER.match(currentFieldName, parser.getDeprecationHandler())) { + clusterPrivileges = readStringArray(name, parser, true); + } else if (Fields.APPLICATIONS.match(currentFieldName, parser.getDeprecationHandler()) + || Fields.APPLICATION.match(currentFieldName, parser.getDeprecationHandler())) { + applicationPrivileges = parseApplicationPrivileges(name, parser); + } else if (Fields.GLOBAL.match(currentFieldName, parser.getDeprecationHandler())) { + configurableClusterPrivileges = ConfigurableClusterPrivileges.parse(parser); + } else if (Fields.METADATA.match(currentFieldName, parser.getDeprecationHandler())) { + if (token != XContentParser.Token.START_OBJECT) { + throw new ElasticsearchParseException( + "expected field [{}] to be of type object, but found [{}] instead", + currentFieldName, + token + ); + } + metadata = parser.map(); + } else if (Fields.TRANSIENT_METADATA.match(currentFieldName, parser.getDeprecationHandler())) { + if (token == XContentParser.Token.START_OBJECT) { + // consume object but just drop + parser.map(); + } else { + throw new ElasticsearchParseException( + "failed to parse role [{}]. unexpected field [{}]", + name, + currentFieldName + ); + } + } else if (Fields.REMOTE_INDICES.match(currentFieldName, parser.getDeprecationHandler())) { + remoteIndicesPrivileges = parseRemoteIndices(name, parser); + } else if (allowRestriction && Fields.RESTRICTION.match(currentFieldName, parser.getDeprecationHandler())) { + restriction = Restriction.parse(name, parser); + } else if (Fields.TYPE.match(currentFieldName, parser.getDeprecationHandler())) { + // don't need it } else { throw new ElasticsearchParseException( "failed to parse role [{}]. unexpected field [{}]", @@ -501,28 +533,22 @@ public static RoleDescriptor parse(String name, XContentParser parser, boolean a currentFieldName ); } - } else if (Fields.REMOTE_INDICES.match(currentFieldName, parser.getDeprecationHandler())) { - remoteIndicesPrivileges = parseRemoteIndices(name, parser); - } else if (allowRestriction && Fields.RESTRICTION.match(currentFieldName, parser.getDeprecationHandler())) { - restriction = Restriction.parse(name, parser); - } else if (Fields.TYPE.match(currentFieldName, parser.getDeprecationHandler())) { - // don't need it - } else { - throw new ElasticsearchParseException("failed to parse role [{}]. unexpected field [{}]", name, currentFieldName); - } + } + return new RoleDescriptor( + name, + clusterPrivileges, + indicesPrivileges, + applicationPrivileges, + configurableClusterPrivileges.toArray(new ConfigurableClusterPrivilege[configurableClusterPrivileges.size()]), + runAsUsers, + metadata, + null, + remoteIndicesPrivileges, + restriction + ); + } - return new RoleDescriptor( - name, - clusterPrivileges, - indicesPrivileges, - applicationPrivileges, - configurableClusterPrivileges.toArray(new ConfigurableClusterPrivilege[configurableClusterPrivileges.size()]), - runAsUsers, - metadata, - null, - remoteIndicesPrivileges, - restriction - ); + } private static String[] readStringArray(String roleName, XContentParser parser, boolean allowNull) throws IOException { diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorsIntersection.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorsIntersection.java index bdfc87a06c922..446209b1d7ac3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorsIntersection.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorsIntersection.java @@ -26,6 +26,8 @@ public record RoleDescriptorsIntersection(Collection> roleDe public static RoleDescriptorsIntersection EMPTY = new RoleDescriptorsIntersection(Collections.emptyList()); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); + public RoleDescriptorsIntersection(RoleDescriptor roleDescriptor) { this(List.of(Set.of(roleDescriptor))); } @@ -70,7 +72,7 @@ public static RoleDescriptorsIntersection fromXContent(XContentParser xContentPa while ((token = p.nextToken()) != XContentParser.Token.END_OBJECT) { XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, p); XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, p.nextToken(), p); - roleDescriptors.add(RoleDescriptor.parse(p.currentName(), p, false)); + roleDescriptors.add(ROLE_DESCRIPTOR_PARSER.parse(p.currentName(), p)); } return Set.copyOf(roleDescriptors); }); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponseTests.java index b473f64a3fbf9..b37b923ce96c5 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/action/service/GetServiceAccountResponseTests.java @@ -97,12 +97,9 @@ private void assertRoleDescriptorEquals(Map responseFragment, Ro @SuppressWarnings("unchecked") final Map descriptorMap = (Map) responseFragment.get("role_descriptor"); assertThat( - RoleDescriptor.parse( - roleDescriptor.getName(), - XContentTestUtils.convertToXContent(descriptorMap, XContentType.JSON), - false, - XContentType.JSON - ), + RoleDescriptor.parserBuilder() + .build() + .parse(roleDescriptor.getName(), XContentTestUtils.convertToXContent(descriptorMap, XContentType.JSON), XContentType.JSON), equalTo(roleDescriptor) ); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorTests.java index 294ad7e975286..efa1dc2e29d10 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/RoleDescriptorTests.java @@ -162,13 +162,16 @@ public void testToXContentRoundtrip() throws Exception { final RoleDescriptor descriptor = randomRoleDescriptor(true, true, true); final XContentType xContentType = randomFrom(XContentType.values()); final BytesReference xContentValue = toShuffledXContent(descriptor, xContentType, ToXContent.EMPTY_PARAMS, false); - final RoleDescriptor parsed = RoleDescriptor.parse(descriptor.getName(), xContentValue, false, xContentType); + final RoleDescriptor parsed = RoleDescriptor.parserBuilder() + .allowRestriction(true) + .build() + .parse(descriptor.getName(), xContentValue, xContentType); assertThat(parsed, equalTo(descriptor)); } public void testParse() throws Exception { String q = "{\"cluster\":[\"a\", \"b\"]}"; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + RoleDescriptor rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(0, rd.getIndicesPrivileges().length); @@ -179,7 +182,7 @@ public void testParse() throws Exception { "cluster": [ "a", "b" ], "run_as": [ "m", "n" ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(0, rd.getIndicesPrivileges().length); @@ -246,7 +249,7 @@ public void testParse() throws Exception { "workflows": ["search_application_query"] } }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().allowRestriction(true).build().parse("test", new BytesArray(q), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(3, rd.getIndicesPrivileges().length); @@ -274,7 +277,7 @@ public void testParse() throws Exception { } } }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(1, rd.getIndicesPrivileges().length); @@ -285,7 +288,7 @@ public void testParse() throws Exception { q = """ {"cluster":["a", "b"], "metadata":{"foo":"bar"}}"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(0, rd.getIndicesPrivileges().length); @@ -327,7 +330,7 @@ public void testParse() throws Exception { } } }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertThat(rd.getName(), equalTo("test")); assertThat(rd.getClusterPrivileges(), arrayContaining("a", "b")); assertThat(rd.getIndicesPrivileges().length, equalTo(1)); @@ -368,7 +371,7 @@ public void testParse() throws Exception { } } }"""; - rd = RoleDescriptor.parse("testUpdateProfile", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("testUpdateProfile", new BytesArray(q), XContentType.JSON); assertThat(rd.getName(), is("testUpdateProfile")); assertThat(rd.getClusterPrivileges(), arrayContaining("manage")); assertThat(rd.getIndicesPrivileges(), Matchers.emptyArray()); @@ -393,7 +396,7 @@ public void testParse() throws Exception { q = """ {"applications": [{"application": "myapp", "resources": ["*"], "privileges": ["login" ]}] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(q), XContentType.JSON); assertThat(rd.getName(), equalTo("test")); assertThat(rd.getClusterPrivileges(), emptyArray()); assertThat(rd.getIndicesPrivileges(), emptyArray()); @@ -407,26 +410,26 @@ public void testParse() throws Exception { {"applications":[{"not_supported": true, "resources": ["*"], "privileges": ["my-app:login" ]}] }"""; final IllegalArgumentException ex = expectThrows( IllegalArgumentException.class, - () -> RoleDescriptor.parse("test", new BytesArray(badJson), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(badJson), XContentType.JSON) ); assertThat(ex.getMessage(), containsString("not_supported")); - rd = RoleDescriptor.parse("test_empty_restriction", new BytesArray(""" + rd = RoleDescriptor.parserBuilder().allowRestriction(true).build().parse("test_empty_restriction", new BytesArray(""" { "index": [{"names": "idx1", "privileges": [ "p1", "p2" ]}], "restriction":{} - }"""), false, XContentType.JSON); + }"""), XContentType.JSON); assertThat(rd.getName(), equalTo("test_empty_restriction")); assertThat(rd.hasRestriction(), equalTo(false)); assertThat(rd.hasWorkflowsRestriction(), equalTo(false)); final ElasticsearchParseException pex1 = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test_null_workflows", new BytesArray(""" + () -> RoleDescriptor.parserBuilder().allowRestriction(true).build().parse("test_null_workflows", new BytesArray(""" { "index": [{"names": ["idx1"], "privileges": [ "p1", "p2" ]}], "restriction":{"workflows":null} - }"""), false, XContentType.JSON) + }"""), XContentType.JSON) ); assertThat( pex1.getMessage(), @@ -438,11 +441,11 @@ public void testParse() throws Exception { final ElasticsearchParseException pex2 = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test_empty_workflows", new BytesArray(""" + () -> RoleDescriptor.parserBuilder().allowRestriction(true).build().parse("test_empty_workflows", new BytesArray(""" { "index": [{"names": ["idx1"], "privileges": [ "p1", "p2" ]}], "restriction":{"workflows":[]} - }"""), false, XContentType.JSON) + }"""), XContentType.JSON) ); assertThat( pex2.getMessage(), @@ -477,7 +480,7 @@ public void testParsingFieldPermissionsUsesCache() throws IOException { ] } """; - RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); final int numberOfFieldSecurityBlocks = 2; final Cache.CacheStats betweenStats = fieldPermissionsCache.getCacheStats(); @@ -486,7 +489,7 @@ public void testParsingFieldPermissionsUsesCache() throws IOException { final int iterations = randomIntBetween(1, 5); for (int i = 0; i < iterations; i++) { - RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); } final Cache.CacheStats afterStats = fieldPermissionsCache.getCacheStats(); @@ -608,12 +611,13 @@ public void testParseRoleWithRestrictionFailsWhenAllowRestrictionIsFalse() { }"""; final ElasticsearchParseException e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse( - "test_role_with_restriction", - XContentHelper.createParser(XContentParserConfiguration.EMPTY, new BytesArray(json), XContentType.JSON), - randomBoolean(), - false - ) + () -> RoleDescriptor.parserBuilder() + .allowRestriction(false) + .build() + .parse( + "test_role_with_restriction", + XContentHelper.createParser(XContentParserConfiguration.EMPTY, new BytesArray(json), XContentType.JSON) + ) ); assertThat( e, @@ -630,12 +634,13 @@ public void testParseRoleWithRestrictionWhenAllowRestrictionIsTrue() throws IOEx "workflows": ["search_application"] } }"""; - RoleDescriptor role = RoleDescriptor.parse( - "test_role_with_restriction", - XContentHelper.createParser(XContentParserConfiguration.EMPTY, new BytesArray(json), XContentType.JSON), - randomBoolean(), - true - ); + RoleDescriptor role = RoleDescriptor.parserBuilder() + .allowRestriction(true) + .build() + .parse( + "test_role_with_restriction", + XContentHelper.createParser(XContentParserConfiguration.EMPTY, new BytesArray(json), XContentType.JSON) + ); assertThat(role.getName(), equalTo("test_role_with_restriction")); assertThat(role.hasRestriction(), equalTo(true)); assertThat(role.hasWorkflowsRestriction(), equalTo(true)); @@ -655,7 +660,7 @@ public void testParseEmptyQuery() throws Exception { } ] }"""; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + RoleDescriptor rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(1, rd.getIndicesPrivileges().length); @@ -677,7 +682,7 @@ public void testParseNullQuery() throws Exception { } ] }"""; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + RoleDescriptor rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(1, rd.getIndicesPrivileges().length); @@ -699,7 +704,7 @@ public void testParseEmptyQueryUsingDeprecatedIndicesField() throws Exception { } ] }"""; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + RoleDescriptor rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); assertEquals("test", rd.getName()); assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(1, rd.getIndicesPrivileges().length); @@ -721,7 +726,7 @@ public void testParseIgnoresTransientMetadata() throws Exception { ); XContentBuilder b = jsonBuilder(); descriptor.toXContent(b, ToXContent.EMPTY_PARAMS); - RoleDescriptor parsed = RoleDescriptor.parse("test", BytesReference.bytes(b), false, XContentType.JSON); + RoleDescriptor parsed = RoleDescriptor.parserBuilder().build().parse("test", BytesReference.bytes(b), XContentType.JSON); assertNotNull(parsed); assertEquals(1, parsed.getTransientMetadata().size()); assertEquals(true, parsed.getTransientMetadata().get("enabled")); @@ -745,7 +750,7 @@ public void testParseIndicesPrivilegesSucceedsWhenExceptFieldsIsSubsetOfGrantedF } ] }""", grant, except); - final RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON); + final RoleDescriptor rd = RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON); assertEquals("test", rd.getName()); assertEquals(1, rd.getIndicesPrivileges().length); assertArrayEquals(new String[] { "idx1", "idx2" }, rd.getIndicesPrivileges()[0].getIndices()); @@ -774,7 +779,7 @@ public void testParseIndicesPrivilegesFailsWhenExceptFieldsAreNotSubsetOfGranted }"""; final ElasticsearchParseException epe = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON) ); assertThat(epe, TestMatchers.throwableWithMessage(containsString("must be a subset of the granted fields "))); assertThat(epe, TestMatchers.throwableWithMessage(containsString("f1"))); @@ -794,7 +799,7 @@ public void testParseRemoteIndicesPrivilegesFailsWhenClustersFieldMissing() { }"""; final ElasticsearchParseException epe = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON) ); assertThat( epe, @@ -817,7 +822,7 @@ public void testParseIndicesPrivilegesFailsWhenClustersFieldPresent() { }"""; final ElasticsearchParseException epe = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(json), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder().build().parse("test", new BytesArray(json), XContentType.JSON) ); assertThat( epe, @@ -961,7 +966,7 @@ public void testGlobalPrivilegesOrdering() throws IOException { } } }""", profileNamesString, applicationNamesString); - RoleDescriptor role3 = RoleDescriptor.parse(roleName, new BytesArray(json), false, XContentType.JSON); + RoleDescriptor role3 = RoleDescriptor.parserBuilder().build().parse(roleName, new BytesArray(json), XContentType.JSON); assertThat(role3, is(role1)); json = Strings.format(""" { @@ -978,7 +983,7 @@ public void testGlobalPrivilegesOrdering() throws IOException { } } }""", applicationNamesString, profileNamesString); - RoleDescriptor role4 = RoleDescriptor.parse(roleName, new BytesArray(json), false, XContentType.JSON); + RoleDescriptor role4 = RoleDescriptor.parserBuilder().build().parse(roleName, new BytesArray(json), XContentType.JSON); assertThat(role4, is(role1)); } diff --git a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/role/RoleWithRemoteIndicesPrivilegesRestIT.java b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/role/RoleWithRemoteIndicesPrivilegesRestIT.java index 4e3a520678f70..d76902efc35b5 100644 --- a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/role/RoleWithRemoteIndicesPrivilegesRestIT.java +++ b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/role/RoleWithRemoteIndicesPrivilegesRestIT.java @@ -323,7 +323,7 @@ private void expectRoleDescriptorInResponse(final Response getRoleResponse, fina throws IOException { final Map actual = responseAsParser(getRoleResponse).map( HashMap::new, - p -> RoleDescriptor.parse(expectedRoleDescriptor.getName(), p, false) + p -> RoleDescriptor.parserBuilder().build().parse(expectedRoleDescriptor.getName(), p) ); assertThat(actual, equalTo(Map.of(expectedRoleDescriptor.getName(), expectedRoleDescriptor))); } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java index 0a8fbb1ecffc0..b8f6551f36037 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java @@ -2874,12 +2874,14 @@ private void expectRoleDescriptorsForApiKey( for (RoleDescriptor expectedRoleDescriptor : expectedRoleDescriptors) { assertThat(rawRoleDescriptor, hasKey(expectedRoleDescriptor.getName())); final var descriptor = (Map) rawRoleDescriptor.get(expectedRoleDescriptor.getName()); - final var roleDescriptor = RoleDescriptor.parse( - expectedRoleDescriptor.getName(), - XContentTestUtils.convertToXContent(descriptor, XContentType.JSON), - false, - XContentType.JSON - ); + final var roleDescriptor = RoleDescriptor.parserBuilder() + .allowRestriction(true) + .build() + .parse( + expectedRoleDescriptor.getName(), + XContentTestUtils.convertToXContent(descriptor, XContentType.JSON), + XContentType.JSON + ); assertEquals(expectedRoleDescriptor, roleDescriptor); } } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java index fe9c1f37e7d49..f4a314c55acfc 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java @@ -247,12 +247,9 @@ public void testServiceAccountApiKey() throws IOException { @SuppressWarnings("unchecked") final Map descriptor = (Map) fleetServerRoleDescriptor.get("elastic/fleet-server"); - final RoleDescriptor roleDescriptor = RoleDescriptor.parse( - "elastic/fleet-server", - XContentTestUtils.convertToXContent(descriptor, XContentType.JSON), - false, - XContentType.JSON - ); + final RoleDescriptor roleDescriptor = RoleDescriptor.parserBuilder() + .build() + .parse("elastic/fleet-server", XContentTestUtils.convertToXContent(descriptor, XContentType.JSON), XContentType.JSON); assertThat(roleDescriptor, equalTo(ServiceAccountService.getServiceAccounts().get("elastic/fleet-server").roleDescriptor())); } @@ -588,12 +585,13 @@ public void testCreateCrossClusterApiKey() throws IOException { final Map roleDescriptors = (Map) document.get("role_descriptors"); assertThat(roleDescriptors.keySet(), contains("cross_cluster")); @SuppressWarnings("unchecked") - final RoleDescriptor actualRoleDescriptor = RoleDescriptor.parse( - "cross_cluster", - XContentTestUtils.convertToXContent((Map) roleDescriptors.get("cross_cluster"), XContentType.JSON), - false, - XContentType.JSON - ); + final RoleDescriptor actualRoleDescriptor = RoleDescriptor.parserBuilder() + .build() + .parse( + "cross_cluster", + XContentTestUtils.convertToXContent((Map) roleDescriptors.get("cross_cluster"), XContentType.JSON), + XContentType.JSON + ); final RoleDescriptor expectedRoleDescriptor = new RoleDescriptor( "cross_cluster", diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java index ffacd72b05abf..e4436d4fabe71 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java @@ -202,6 +202,8 @@ public class ApiKeyService { Property.NodeScope ); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allowRestriction(true).build(); + private final Clock clock; private final Client client; private final SecurityIndexManager securityIndex; @@ -987,7 +989,7 @@ public List parseRoleDescriptors( XContentType.JSON ) ) { - return RoleDescriptor.parse(name, parser, false); + return ROLE_DESCRIPTOR_PARSER.parse(name, parser); } } catch (IOException e) { throw new UncheckedIOException(e); @@ -1027,7 +1029,7 @@ private static List parseRoleDescriptorsBytes( while (parser.nextToken() != XContentParser.Token.END_OBJECT) { parser.nextToken(); // role name String roleName = parser.currentName(); - roleDescriptors.add(RoleDescriptor.parse(roleName, parser, false)); + roleDescriptors.add(ROLE_DESCRIPTOR_PARSER.parse(roleName, parser)); } } catch (IOException e) { throw new UncheckedIOException(e); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java index d769e44f2d38d..368ec3825c0c2 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java @@ -67,6 +67,7 @@ public class FileRolesStore implements BiConsumer, ActionListener, ActionListener< private static final Logger logger = LogManager.getLogger(NativeRolesStore.class); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder().allow2xFormat(true).build(); + private final Settings settings; private final Client client; private final XPackLicenseState licenseState; @@ -482,9 +484,8 @@ static RoleDescriptor transformRole(String id, BytesReference sourceBytes, Logge assert id.startsWith(ROLE_TYPE) : "[" + id + "] does not have role prefix"; final String name = id.substring(ROLE_TYPE.length() + 1); try { - // we pass true as allow2xFormat parameter because we do not want to reject permissions if the field permissions - // are given in 2.x syntax - RoleDescriptor roleDescriptor = RoleDescriptor.parse(name, sourceBytes, true, XContentType.JSON, false); + // we do not want to reject permissions if the field permissions are given in 2.x syntax, hence why we allow2xFormat + RoleDescriptor roleDescriptor = ROLE_DESCRIPTOR_PARSER.parse(name, sourceBytes, XContentType.JSON); final boolean dlsEnabled = Arrays.stream(roleDescriptor.getIndicesPrivileges()) .anyMatch(IndicesPrivileges::isUsingDocumentLevelSecurity); final boolean flsEnabled = Arrays.stream(roleDescriptor.getIndicesPrivileges()) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/apikey/RestGrantApiKeyAction.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/apikey/RestGrantApiKeyAction.java index 8fda0f0518c93..572cf70586f7d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/apikey/RestGrantApiKeyAction.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/apikey/RestGrantApiKeyAction.java @@ -50,7 +50,10 @@ public interface RequestTranslator { GrantApiKeyRequest translate(RestRequest request) throws IOException; class Default implements RequestTranslator { - private static final ObjectParser PARSER = createParser((n, p) -> RoleDescriptor.parse(n, p, false)); + private static final RoleDescriptor.Parser ROLE_DESCRIPTOR_PARSER = RoleDescriptor.parserBuilder() + .allowRestriction(true) + .build(); + private static final ObjectParser PARSER = createParser(ROLE_DESCRIPTOR_PARSER::parse); protected static ObjectParser createParser( CheckedBiFunction roleDescriptorParser diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/TestSecurityClient.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/TestSecurityClient.java index 4888c0f4c9721..e8eb50e3a6529 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/test/TestSecurityClient.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/test/TestSecurityClient.java @@ -212,7 +212,7 @@ private Map getRoleDescriptors(String roleParameter) thr XContentParserUtils.ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser); final String roleName = parser.currentName(); XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser); - final RoleDescriptor role = RoleDescriptor.parse(roleName, parser, false); + final RoleDescriptor role = RoleDescriptor.parserBuilder().build().parse(roleName, parser); roles.put(roleName, role); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java index 9f060dd102a04..269031804f7e3 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java @@ -1866,7 +1866,10 @@ public void testApiKeyDocCache() throws IOException, ExecutionException, Interru final String apiKey3 = randomAlphaOfLength(16); ApiKeyCredentials apiKeyCredentials3 = getApiKeyCredentials(docId3, apiKey3, type); final List keyRoles = List.of( - RoleDescriptor.parse("key-role", new BytesArray("{\"cluster\":[\"monitor\"]}"), true, XContentType.JSON) + RoleDescriptor.parserBuilder() + .allow2xFormat(true) + .build() + .parse("key-role", new BytesArray("{\"cluster\":[\"monitor\"]}"), XContentType.JSON) ); final Map metadata3 = mockKeyDocument( docId3, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/FieldPermissionsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/FieldPermissionsTests.java index b97d71466f181..fc5374fb324ac 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/FieldPermissionsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/permission/FieldPermissionsTests.java @@ -37,7 +37,8 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + RoleDescriptor.Parser roleParser = RoleDescriptor.parserBuilder().build(); + RoleDescriptor rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" }); assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" }); @@ -54,7 +55,7 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" }); assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" }); @@ -70,7 +71,7 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2" }); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); @@ -86,7 +87,7 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {}); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); @@ -103,7 +104,7 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {}); assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] {}); @@ -121,7 +122,7 @@ public void testParseFieldPermissions() throws Exception { }"""; ElasticsearchParseException e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(exceptWithoutGrant), false, XContentType.JSON) + () -> roleParser.parse("test", new BytesArray(exceptWithoutGrant), XContentType.JSON) ); assertThat( e.getDetailedMessage(), @@ -130,10 +131,7 @@ public void testParseFieldPermissions() throws Exception { final String grantNull = """ {"indices": [ {"names": "idx2", "privileges": ["p3"], "field_security": {"grant": null}}]}"""; - e = expectThrows( - ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(grantNull), false, XContentType.JSON) - ); + e = expectThrows(ElasticsearchParseException.class, () -> roleParser.parse("test", new BytesArray(grantNull), XContentType.JSON)); assertThat( e.getDetailedMessage(), containsString("failed to parse indices privileges for" + " role [test]. grant must not be null.") @@ -141,10 +139,7 @@ public void testParseFieldPermissions() throws Exception { final String exceptNull = """ {"indices": [ {"names": "idx2", "privileges": ["p3"], "field_security": {"grant": ["*"],"except": null}}]}"""; - e = expectThrows( - ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(exceptNull), false, XContentType.JSON) - ); + e = expectThrows(ElasticsearchParseException.class, () -> roleParser.parse("test", new BytesArray(exceptNull), XContentType.JSON)); assertThat( e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. except must" + " not be null.") @@ -154,7 +149,7 @@ public void testParseFieldPermissions() throws Exception { {"indices": [ {"names": "idx2", "privileges": ["p3"], "field_security": {"grant": null,"except": null}}]}"""; e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(exceptGrantNull), false, XContentType.JSON) + () -> roleParser.parse("test", new BytesArray(exceptGrantNull), XContentType.JSON) ); assertThat( e.getDetailedMessage(), @@ -165,7 +160,7 @@ public void testParseFieldPermissions() throws Exception { {"indices": [ {"names": "idx2", "privileges": ["p3"], "field_security": {}}]}"""; e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(bothFieldsMissing), false, XContentType.JSON) + () -> roleParser.parse("test", new BytesArray(bothFieldsMissing), XContentType.JSON) ); assertThat( e.getDetailedMessage(), @@ -193,7 +188,7 @@ public void testParseFieldPermissions() throws Exception { } ] }"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON); + rd = roleParser.parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {}); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); assertArrayEquals(rd.getIndicesPrivileges()[1].getGrantedFields(), new String[] { "*" }); @@ -204,14 +199,14 @@ public void testParseFieldPermissions() throws Exception { public void testBWCFieldPermissions() throws Exception { String q = """ {"indices": [ {"names": "idx2", "privileges": ["p3"], "fields": ["f1", "f2"]}]}"""; - RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), true, XContentType.JSON); + RoleDescriptor rd = RoleDescriptor.parserBuilder().allow2xFormat(true).build().parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2" }); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); final String failingQuery = q; ElasticsearchParseException e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(failingQuery), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder().allow2xFormat(false).build().parse("test", new BytesArray(failingQuery), XContentType.JSON) ); assertThat(e.getDetailedMessage(), containsString(""" ["fields": [...]] format has changed for field permissions in role [test], \ @@ -219,13 +214,16 @@ public void testBWCFieldPermissions() throws Exception { q = """ {"indices": [ {"names": "idx2", "privileges": ["p3"], "fields": []}]}"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), true, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().allow2xFormat(true).build().parse("test", new BytesArray(q), XContentType.JSON); assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {}); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); final String failingQuery2 = q; e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(failingQuery2), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder() + .allow2xFormat(false) + .build() + .parse("test", new BytesArray(failingQuery2), XContentType.JSON) ); assertThat(e.getDetailedMessage(), containsString(""" ["fields": [...]] format has changed for field permissions in role [test], \ @@ -233,13 +231,16 @@ public void testBWCFieldPermissions() throws Exception { q = """ {"indices": [ {"names": "idx2", "privileges": ["p3"], "fields": null}]}"""; - rd = RoleDescriptor.parse("test", new BytesArray(q), true, XContentType.JSON); + rd = RoleDescriptor.parserBuilder().allow2xFormat(true).build().parse("test", new BytesArray(q), XContentType.JSON); assertNull(rd.getIndicesPrivileges()[0].getGrantedFields()); assertNull(rd.getIndicesPrivileges()[0].getDeniedFields()); final String failingQuery3 = q; e = expectThrows( ElasticsearchParseException.class, - () -> RoleDescriptor.parse("test", new BytesArray(failingQuery3), false, XContentType.JSON) + () -> RoleDescriptor.parserBuilder() + .allow2xFormat(false) + .build() + .parse("test", new BytesArray(failingQuery3), XContentType.JSON) ); assertThat(e.getDetailedMessage(), containsString(""" ["fields": [...]] format has changed for field permissions in role [test], \