Skip to content

Commit

Permalink
Fix docs and error message for role field_security (#82179)
Browse files Browse the repository at this point in the history
In the index permission block of a role descriptor, the "field_security"
field is an object with this format:     "field_security": {
"grant" : [ "field-1", "field-2", "more-fields-*" ],        "except" : [
"more-field-secret-*" ]     } The docs incorrectly stated that
"field_security" was a list, and if you provided a list the parser would
fail with a message that incorrectly stated that START_ARRAY was an
acceptable token. These have both been fixed. While reviewing the test
cases for RoleDescriptor, I also introduced more randomisation to
increase the overall coverage of features and scenarios.

Backport of: #81283
  • Loading branch information
tvernum committed Jan 4, 2022
1 parent daa19ea commit 9961784
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 74 deletions.
4 changes: 2 additions & 2 deletions x-pack/docs/en/rest-api/security/create-roles.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ and pertain to adding a role:
`application` (required)::: (string) The name of the application to which this entry applies
`privileges`::: (list) A list of strings, where each element is the name of an application
privilege or action.
`resources`::: (list) A list resources to which the privileges are applied.
`resources`::: (list) A list resources to which the privileges are applied.

`cluster`:: (list) A list of cluster privileges. These privileges define the
cluster level actions that users with this role are able to execute.
Expand All @@ -56,7 +56,7 @@ is currently limited to the management of application privileges.
This field is optional.

`indices`:: (list) A list of indices permissions entries.
`field_security`::: (list) The document fields that the owners of the role have
`field_security`::: (object) The document fields that the owners of the role have
read access to. For more information, see
<<field-and-document-access-control>>.
`names` (required)::: (list) A list of indices (or index name patterns) to which the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,10 +596,9 @@ private static RoleDescriptor.IndicesPrivileges parseIndex(String roleName, XCon
} while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT);
} else {
throw new ElasticsearchParseException(
"failed to parse indices privileges for role [{}]. expected {} or {} but got {}" + " in \"{}\".",
"failed to parse indices privileges for role [{}]. expected {} but got {} in \"{}\".",
roleName,
XContentParser.Token.START_OBJECT,
XContentParser.Token.START_ARRAY,
token,
Fields.FIELD_PERMISSIONS
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.TestMatchers;
import org.elasticsearch.test.VersionUtils;
Expand All @@ -25,8 +26,11 @@
import org.elasticsearch.xcontent.XContentType;
import org.elasticsearch.xpack.core.XPackClientPlugin;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
import org.hamcrest.Matchers;

Expand Down Expand Up @@ -69,7 +73,7 @@ public void testEqualsOnEmptyRoles() {
"null_role",
randomFrom((String[]) null, new String[0]),
randomFrom((RoleDescriptor.IndicesPrivileges[]) null, new RoleDescriptor.IndicesPrivileges[0]),
randomFrom((RoleDescriptor.ApplicationResourcePrivileges[]) null, new RoleDescriptor.ApplicationResourcePrivileges[0]),
randomFrom((ApplicationResourcePrivileges[]) null, new ApplicationResourcePrivileges[0]),
randomFrom((ConfigurableClusterPrivilege[]) null, new ConfigurableClusterPrivilege[0]),
randomFrom((String[]) null, new String[0]),
randomFrom((Map<String, Object>) null, new HashMap<>()),
Expand All @@ -86,12 +90,8 @@ public void testToString() {
.grantedFields("body", "title")
.query("{\"match_all\": {}}")
.build() };
final RoleDescriptor.ApplicationResourcePrivileges[] applicationPrivileges = {
RoleDescriptor.ApplicationResourcePrivileges.builder()
.application("my_app")
.privileges("read", "write")
.resources("*")
.build() };
final ApplicationResourcePrivileges[] applicationPrivileges = {
ApplicationResourcePrivileges.builder().application("my_app").privileges("read", "write").resources("*").build() };

final ConfigurableClusterPrivilege[] configurableClusterPrivileges = new ConfigurableClusterPrivilege[] {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02"))) };
Expand Down Expand Up @@ -120,42 +120,15 @@ public void testToString() {
);
}

public void testToXContent() throws Exception {
RoleDescriptor.IndicesPrivileges[] groups = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices("i1", "i2")
.privileges("read")
.grantedFields("body", "title")
.allowRestrictedIndices(randomBoolean())
.query("{\"match_all\": {}}")
.build() };
final RoleDescriptor.ApplicationResourcePrivileges[] applicationPrivileges = {
RoleDescriptor.ApplicationResourcePrivileges.builder()
.application("my_app")
.privileges("read", "write")
.resources("*")
.build() };
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02"))) };

Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
RoleDescriptor descriptor = new RoleDescriptor(
"test",
new String[] { "all", "none" },
groups,
applicationPrivileges,
configurableClusterPrivileges,
new String[] { "sudo" },
metadata,
Collections.emptyMap()
);
XContentBuilder builder = descriptor.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS);
RoleDescriptor parsed = RoleDescriptor.parse("test", BytesReference.bytes(builder), false, XContentType.JSON);
public void testToXContentRoundtrip() throws Exception {
final RoleDescriptor descriptor = randomRoleDescriptor();
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);
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);
assertEquals("test", rd.getName());
Expand Down Expand Up @@ -260,33 +233,8 @@ public void testSerialization() throws Exception {
logger.info("Testing serialization with version {}", version);
BytesStreamOutput output = new BytesStreamOutput();
output.setVersion(version);
RoleDescriptor.IndicesPrivileges[] groups = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices("i1", "i2")
.privileges("read")
.grantedFields("body", "title")
.query("{\"query\": {\"match_all\": {}}}")
.build() };
final RoleDescriptor.ApplicationResourcePrivileges[] applicationPrivileges = {
RoleDescriptor.ApplicationResourcePrivileges.builder()
.application("my_app")
.privileges("read", "write")
.resources("*")
.build() };
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02"))) };

Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
final RoleDescriptor descriptor = new RoleDescriptor(
"test",
new String[] { "all", "none" },
groups,
applicationPrivileges,
configurableClusterPrivileges,
new String[] { "sudo" },
metadata,
null
);
final RoleDescriptor descriptor = randomRoleDescriptor();
descriptor.writeTo(output);
final NamedWriteableRegistry registry = new NamedWriteableRegistry(new XPackClientPlugin(Settings.EMPTY).getNamedWriteables());
StreamInput streamInput = new NamedWriteableAwareStreamInput(
Expand All @@ -295,7 +243,7 @@ public void testSerialization() throws Exception {
);
streamInput.setVersion(version);
final RoleDescriptor serialized = new RoleDescriptor(streamInput);
assertEquals(descriptor, serialized);
assertThat(serialized, equalTo(descriptor));
}

public void testParseEmptyQuery() throws Exception {
Expand Down Expand Up @@ -396,7 +344,7 @@ public void testIsEmpty() {
randomAlphaOfLengthBetween(1, 10),
new String[0],
new RoleDescriptor.IndicesPrivileges[0],
new RoleDescriptor.ApplicationResourcePrivileges[0],
new ApplicationResourcePrivileges[0],
new ConfigurableClusterPrivilege[0],
new String[0],
new HashMap<>(),
Expand All @@ -421,9 +369,9 @@ public void testIsEmpty() {
: new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices("idx").privileges("foo").build() },
booleans.get(2)
? new RoleDescriptor.ApplicationResourcePrivileges[0]
: new RoleDescriptor.ApplicationResourcePrivileges[] {
RoleDescriptor.ApplicationResourcePrivileges.builder().application("app").privileges("foo").resources("res").build() },
? new ApplicationResourcePrivileges[0]
: new ApplicationResourcePrivileges[] {
ApplicationResourcePrivileges.builder().application("app").privileges("foo").resources("res").build() },
booleans.get(3)
? new ConfigurableClusterPrivilege[0]
: new ConfigurableClusterPrivilege[] {
Expand All @@ -439,4 +387,75 @@ public void testIsEmpty() {
assertTrue(roleDescriptor.isEmpty());
}
}

private RoleDescriptor randomRoleDescriptor() {
final RoleDescriptor.IndicesPrivileges[] indexPrivileges = new RoleDescriptor.IndicesPrivileges[randomIntBetween(0, 3)];
for (int i = 0; i < indexPrivileges.length; i++) {
final RoleDescriptor.IndicesPrivileges.Builder builder = RoleDescriptor.IndicesPrivileges.builder()
.privileges(randomSubsetOf(randomIntBetween(1, 4), IndexPrivilege.names()))
.indices(generateRandomStringArray(5, randomIntBetween(3, 9), false, false))
.allowRestrictedIndices(randomBoolean());
if (randomBoolean()) {
builder.query(
randomBoolean()
? "{ \"term\": { \"" + randomAlphaOfLengthBetween(3, 24) + "\" : \"" + randomAlphaOfLengthBetween(3, 24) + "\" }"
: "{ \"match_all\": {} }"
);
}
if (randomBoolean()) {
if (randomBoolean()) {
builder.grantedFields("*");
builder.deniedFields(generateRandomStringArray(4, randomIntBetween(4, 9), false, false));
} else {
builder.grantedFields(generateRandomStringArray(4, randomIntBetween(4, 9), false, false));
}
}
indexPrivileges[i] = builder.build();
}
final ApplicationResourcePrivileges[] applicationPrivileges = new ApplicationResourcePrivileges[randomIntBetween(0, 2)];
for (int i = 0; i < applicationPrivileges.length; i++) {
final ApplicationResourcePrivileges.Builder builder = ApplicationResourcePrivileges.builder();
builder.application(randomAlphaOfLengthBetween(5, 12) + (randomBoolean() ? "*" : ""));
if (randomBoolean()) {
builder.privileges("*");
} else {
builder.privileges(generateRandomStringArray(6, randomIntBetween(4, 8), false, false));
}
if (randomBoolean()) {
builder.resources("*");
} else {
builder.resources(generateRandomStringArray(6, randomIntBetween(4, 8), false, false));
}
applicationPrivileges[i] = builder.build();
}
final ConfigurableClusterPrivilege[] configurableClusterPrivileges;
if (randomBoolean()) {
configurableClusterPrivileges = new ConfigurableClusterPrivilege[] {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(
Sets.newHashSet(generateRandomStringArray(3, randomIntBetween(4, 12), false, false))
) };
} else {
configurableClusterPrivileges = new ConfigurableClusterPrivilege[0];
}
final Map<String, Object> metadata = new HashMap<>();
while (randomBoolean()) {
String key = randomAlphaOfLengthBetween(4, 12);
if (randomBoolean()) {
key = MetadataUtils.RESERVED_PREFIX + key;
}
final Object value = randomBoolean() ? randomInt() : randomAlphaOfLengthBetween(3, 50);
metadata.put(key, value);
}

return new RoleDescriptor(
randomAlphaOfLengthBetween(3, 90),
randomSubsetOf(ClusterPrivilegeResolver.names()).toArray(new String[0]),
indexPrivileges,
applicationPrivileges,
configurableClusterPrivileges,
generateRandomStringArray(5, randomIntBetween(2, 8), false, true),
metadata,
Collections.emptyMap()
);
}
}

0 comments on commit 9961784

Please sign in to comment.