Fixed build failure related to security roles APIs

- roles are now reliably parsed
- in `Put Role` API, added a double check to verify that the role name in the URL matches the role name if the body. Also, if the body doesn't have a role name, the role name in the URL will be used.

Original commit: elastic/x-pack-elasticsearch@5054ce8567
This commit is contained in:
uboness 2016-02-25 01:15:15 -08:00
parent 8ff6b93a3c
commit 266bf09437
8 changed files with 127 additions and 126 deletions

View File

@ -8,7 +8,6 @@ package org.elasticsearch.shield.action.role;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
@ -18,7 +17,7 @@ import org.elasticsearch.shield.authz.RoleDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import static org.elasticsearch.action.ValidateActions.addValidationError;
@ -29,19 +28,13 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements ToXContent {
private String name;
private List<String> clusterPriv;
// List of index names to privileges
private List<RoleDescriptor.IndicesPrivileges> indices = new ArrayList<>();
private List<String> runAs = new ArrayList<>();
private RoleDescriptor roleDescriptor;
private String[] clusterPrivileges;
private List<RoleDescriptor.IndicesPrivileges> indicesPrivileges = new ArrayList<>();
private String[] runAs;
public PutRoleRequest() {
}
public PutRoleRequest(BytesReference source) throws Exception {
this.roleDescriptor = RoleDescriptor.source(source);
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
@ -51,20 +44,24 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements ToX
return validationException;
}
public void source(String name, BytesReference source) throws Exception {
RoleDescriptor descriptor = RoleDescriptor.source(name, source);
this.name = descriptor.getName();
this.clusterPrivileges = descriptor.getClusterPrivileges();
this.indicesPrivileges = Arrays.asList(descriptor.getIndicesPrivileges());
this.runAs = descriptor.getRunAs();
}
public void name(String name) {
this.name = name;
}
public void cluster(String clusterPrivilege) {
this.clusterPriv = Collections.singletonList(clusterPrivilege);
}
public void cluster(List<String> clusterPrivileges) {
this.clusterPriv = clusterPrivileges;
public void cluster(String... clusterPrivileges) {
this.clusterPrivileges = clusterPrivileges;
}
public void addIndex(String[] indices, String[] privileges, @Nullable String[] fields, @Nullable BytesReference query) {
this.indices.add(RoleDescriptor.IndicesPrivileges.builder()
this.indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.builder()
.indices(indices)
.privileges(privileges)
.fields(fields)
@ -72,7 +69,7 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements ToX
.build());
}
public void runAs(List<String> usernames) {
public void runAs(String... usernames) {
this.runAs = usernames;
}
@ -80,72 +77,46 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> implements ToX
return name;
}
public List<String> cluster() {
return clusterPriv;
public String[] cluster() {
return clusterPrivileges;
}
public List<RoleDescriptor.IndicesPrivileges> indices() {
return indices;
public RoleDescriptor.IndicesPrivileges[] indices() {
return indicesPrivileges.toArray(new RoleDescriptor.IndicesPrivileges[indicesPrivileges.size()]);
}
public List<String> runAs() {
public String[] runAs() {
return runAs;
}
private RoleDescriptor roleDescriptor() {
if (this.roleDescriptor != null) {
return this.roleDescriptor;
}
this.roleDescriptor = new RoleDescriptor(name, this.clusterPriv.toArray(Strings.EMPTY_ARRAY),
this.indices, this.runAs.toArray(Strings.EMPTY_ARRAY));
return this.roleDescriptor;
return new RoleDescriptor(name, clusterPrivileges,
indicesPrivileges.toArray(new RoleDescriptor.IndicesPrivileges[indicesPrivileges.size()]), runAs);
}
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
name = in.readString();
int clusterSize = in.readVInt();
List<String> tempCluster = new ArrayList<>(clusterSize);
for (int i = 0; i < clusterSize; i++) {
tempCluster.add(in.readString());
}
clusterPriv = tempCluster;
clusterPrivileges = in.readOptionalStringArray();
int indicesSize = in.readVInt();
indices = new ArrayList<>(indicesSize);
indicesPrivileges = new ArrayList<>(indicesSize);
for (int i = 0; i < indicesSize; i++) {
indices.add(RoleDescriptor.IndicesPrivileges.readIndicesPrivileges(in));
}
if (in.readBoolean()) {
int runAsSize = in.readVInt();
runAs = new ArrayList<>(runAsSize);
for (int i = 0; i < runAsSize; i++) {
runAs.add(in.readString());
}
indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.readIndicesPrivileges(in));
}
runAs = in.readOptionalStringArray();
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeString(name);
out.writeVInt(clusterPriv.size());
for (String cluster : clusterPriv) {
out.writeString(cluster);
}
out.writeVInt(indices.size());
for (RoleDescriptor.IndicesPrivileges index : indices) {
out.writeOptionalStringArray(clusterPrivileges);
out.writeVInt(indicesPrivileges.size());
for (RoleDescriptor.IndicesPrivileges index : indicesPrivileges) {
index.writeTo(out);
}
if (runAs.isEmpty() == false) {
out.writeBoolean(true);
out.writeVInt(runAs.size());
for (String runAsUser : runAs) {
out.writeString(runAsUser);
}
} else {
out.writeBoolean(false);
}
out.writeOptionalStringArray(runAs);
}
@Override

View File

@ -10,8 +10,6 @@ import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import java.util.Arrays;
/**
* Builder for requests to add a role to the administrative index
*/
@ -25,18 +23,23 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
super(client, action, new PutRoleRequest());
}
public PutRoleRequestBuilder source(String name, BytesReference source) throws Exception {
request.source(name, source);
return this;
}
public PutRoleRequestBuilder name(String name) {
request.name(name);
return this;
}
public PutRoleRequestBuilder cluster(String... cluster) {
request.cluster(Arrays.asList(cluster));
public PutRoleRequestBuilder cluster(String... clusterPrivileges) {
request.cluster(clusterPrivileges);
return this;
}
public PutRoleRequestBuilder runAs(String... runAsUsers) {
request.runAs(Arrays.asList(runAsUsers));
request.runAs(runAsUsers);
return this;
}

View File

@ -29,14 +29,14 @@ import java.util.List;
public class RoleDescriptor implements ToXContent {
private final String name;
private final String[] clusterPattern;
private final List<IndicesPrivileges> indicesPrivileges;
private final String[] clusterPrivileges;
private final IndicesPrivileges[] indicesPrivileges;
private final String[] runAs;
public RoleDescriptor(String name, String[] clusterPattern,
List<IndicesPrivileges> indicesPrivileges, String[] runAs) {
public RoleDescriptor(String name, String[] clusterPrivileges,
IndicesPrivileges[] indicesPrivileges, String[] runAs) {
this.name = name;
this.clusterPattern = clusterPattern;
this.clusterPrivileges = clusterPrivileges;
this.indicesPrivileges = indicesPrivileges;
this.runAs = runAs;
}
@ -45,11 +45,11 @@ public class RoleDescriptor implements ToXContent {
return this.name;
}
public String[] getClusterPattern() {
return this.clusterPattern;
public String[] getClusterPrivileges() {
return this.clusterPrivileges;
}
public List<IndicesPrivileges> getIndicesPrivileges() {
public IndicesPrivileges[] getIndicesPrivileges() {
return this.indicesPrivileges;
}
@ -149,21 +149,28 @@ public class RoleDescriptor implements ToXContent {
return tempIndices;
}
public static RoleDescriptor source(BytesReference source) throws Exception {
public static RoleDescriptor source(String name, BytesReference source) throws Exception {
try (XContentParser parser = XContentHelper.createParser(source)) {
XContentParser.Token token;
String currentFieldName = null;
String roleName = null;
String roleName = name;
List<IndicesPrivileges> indicesPrivileges = new ArrayList<>();
List<String> runAsUsers = new ArrayList<>();
List<String> tempClusterPriv = new ArrayList<>();
List<String> clusterPrivileges = new ArrayList<>();
parser.nextToken(); // remove object wrapping
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if ("name".equals(currentFieldName)) {
roleName = parser.text();
if (roleName == null) {
// if the role name is not given, we'll parse it from the source
roleName = parser.text();
} else if (roleName.equals(parser.text()) == false) {
// if the given role name is not the same as the parsed role name, we have inconstency and we need to
// throw an error
throw new ElasticsearchParseException("expected role name [{}] but found [{}] instead", roleName, parser.text());
}
} else {
throw new ElasticsearchParseException("unexpected field in add role request [{}]", currentFieldName);
}
@ -180,7 +187,7 @@ public class RoleDescriptor implements ToXContent {
} else if (token == XContentParser.Token.START_ARRAY && "cluster".equals(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token.isValue()) {
tempClusterPriv.add(parser.text());
clusterPrivileges.add(parser.text());
} else {
throw new ElasticsearchParseException("unexpected value parsing cluster privileges [{}]", token);
}
@ -193,8 +200,9 @@ public class RoleDescriptor implements ToXContent {
if (roleName == null) {
throw new ElasticsearchParseException("field [name] required for role description");
}
return new RoleDescriptor(roleName, tempClusterPriv.toArray(Strings.EMPTY_ARRAY),
indicesPrivileges, runAsUsers.toArray(Strings.EMPTY_ARRAY));
return new RoleDescriptor(roleName, clusterPrivileges.toArray(new String[clusterPrivileges.size()]),
indicesPrivileges.toArray(new IndicesPrivileges[indicesPrivileges.size()]),
runAsUsers.toArray(new String[runAsUsers.size()]));
}
}
@ -202,7 +210,7 @@ public class RoleDescriptor implements ToXContent {
public String toString() {
StringBuilder sb = new StringBuilder("Role[");
sb.append("name=").append(name);
sb.append(", cluster=[").append(Strings.arrayToCommaDelimitedString(clusterPattern));
sb.append(", cluster=[").append(Strings.arrayToCommaDelimitedString(clusterPrivileges));
sb.append("], indicesPrivileges=[");
for (IndicesPrivileges group : indicesPrivileges) {
sb.append(group.toString()).append(",");
@ -216,8 +224,8 @@ public class RoleDescriptor implements ToXContent {
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field("name", name);
builder.field("cluster", clusterPattern);
builder.field("indices", indicesPrivileges);
builder.field("cluster", clusterPrivileges);
builder.field("indices", (Object[]) indicesPrivileges);
if (runAs != null) {
builder.field("run_as", runAs);
}
@ -236,17 +244,17 @@ public class RoleDescriptor implements ToXContent {
indicesPrivileges.add(group);
}
String[] runAs = in.readStringArray();
return new RoleDescriptor(name, clusterPattern, indicesPrivileges, runAs);
return new RoleDescriptor(name, clusterPattern, indicesPrivileges.toArray(new IndicesPrivileges[indicesPrivileges.size()]), runAs);
}
public static void writeTo(RoleDescriptor descriptor, StreamOutput out) throws IOException {
out.writeString(descriptor.getName());
out.writeStringArray(descriptor.getClusterPattern());
out.writeVInt(descriptor.getIndicesPrivileges().size());
for (IndicesPrivileges group : descriptor.getIndicesPrivileges()) {
out.writeString(descriptor.name);
out.writeStringArray(descriptor.clusterPrivileges);
out.writeVInt(descriptor.indicesPrivileges.length);
for (IndicesPrivileges group : descriptor.indicesPrivileges) {
group.writeTo(out);
}
out.writeStringArray(descriptor.getRunAs());
out.writeStringArray(descriptor.runAs);
}
public static class IndicesPrivilegesBuilder {

View File

@ -110,7 +110,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore,
@Nullable
private RoleDescriptor transformRole(BytesReference sourceBytes) {
try {
return RoleDescriptor.source(sourceBytes);
return RoleDescriptor.source(null, sourceBytes);
} catch (Exception e) {
logger.warn("unable to deserialize role from response", e);
return null;

View File

@ -68,7 +68,7 @@ public class Role extends GlobalPermission {
private Builder(RoleDescriptor rd) {
this.name = rd.getName();
this.cluster(ClusterPrivilege.get((new Privilege.Name(rd.getClusterPattern()))));
this.cluster(ClusterPrivilege.get((new Privilege.Name(rd.getClusterPrivileges()))));
for (RoleDescriptor.IndicesPrivileges iGroup : rd.getIndicesPrivileges()) {
this.add(iGroup.getFields() == null ? null : Arrays.asList(iGroup.getFields()),
iGroup.getQuery(),

View File

@ -169,6 +169,10 @@ public class SecurityClient {
return new PutRoleRequestBuilder(client).name(name);
}
public PutRoleRequestBuilder preparePutRole(String name, BytesReference source) throws Exception {
return new PutRoleRequestBuilder(client).source(name, source);
}
public void putRole(PutRoleRequest request, ActionListener<PutRoleResponse> listener) {
client.execute(PutRoleAction.INSTANCE, request, listener);
}

View File

@ -34,14 +34,15 @@ public class RestPutRoleAction extends BaseRestHandler {
@Override
protected void handleRequest(RestRequest request, final RestChannel channel, Client client) throws Exception {
new SecurityClient(client).preparePutRole(request.param("name")).execute(new RestBuilderListener<PutRoleResponse>(channel) {
@Override
public RestResponse buildResponse(PutRoleResponse putRoleResponse, XContentBuilder builder) throws Exception {
return new BytesRestResponse(RestStatus.OK,
builder.startObject()
.field("role", putRoleResponse)
.endObject());
}
});
new SecurityClient(client).preparePutRole(request.param("name"), request.content()).execute(
new RestBuilderListener<PutRoleResponse>(channel) {
@Override
public RestResponse buildResponse(PutRoleResponse putRoleResponse, XContentBuilder builder) throws Exception {
return new BytesRestResponse(RestStatus.OK,
builder.startObject()
.field("role", putRoleResponse)
.endObject());
}
});
}
}

View File

@ -12,10 +12,8 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
import java.util.List;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.core.Is.is;
public class RoleDescriptorTests extends ESTestCase {
@ -30,13 +28,14 @@ public class RoleDescriptorTests extends ESTestCase {
}
public void testRDJson() throws Exception {
List<RoleDescriptor.IndicesPrivileges> groups = new ArrayList<>();
groups.add(RoleDescriptor.IndicesPrivileges.builder()
.indices(new String[]{"i1", "i2"})
.privileges(new String[]{"read"})
.fields(new String[]{"body", "title"})
.query(new BytesArray("{\"query\": {\"match_all\": {}}}"))
.build());
RoleDescriptor.IndicesPrivileges[] groups = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices(new String[] { "i1", "i2" })
.privileges(new String[] { "read" })
.fields(new String[] { "body", "title" })
.query(new BytesArray("{\"query\": {\"match_all\": {}}}"))
.build()
};
RoleDescriptor d = new RoleDescriptor("test", new String[]{"all", "none"}, groups, new String[]{"sudo"});
assertEquals("Role[name=test, cluster=[all,none], indicesPrivileges=[IndicesPrivileges[privileges=[read], indices=[i1,i2], " +
"fields=[body,title], query={\"query\": {\"match_all\": {}}}],], runAs=[sudo]]", d.toString());
@ -53,48 +52,48 @@ public class RoleDescriptorTests extends ESTestCase {
RoleDescriptor rd;
try {
q = "{}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
fail("should have failed");
} catch (ElasticsearchParseException e) {
// expected
}
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
assertEquals("test", rd.getName());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPattern());
assertEquals(0, rd.getIndicesPrivileges().size());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges());
assertEquals(0, rd.getIndicesPrivileges().length);
assertArrayEquals(Strings.EMPTY_ARRAY, rd.getRunAs());
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
assertEquals("test", rd.getName());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPattern());
assertEquals(0, rd.getIndicesPrivileges().size());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges());
assertEquals(0, rd.getIndicesPrivileges().length);
assertArrayEquals(new String[]{"m", "n"}, rd.getRunAs());
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"], \"indices\": [{\"names\": \"idx1\", " +
"\"privileges\": [\"p1\", \"p2\"]}, {\"names\": \"idx2\", \"privileges\": [\"p3\"], \"fields\": [\"f1\", \"f2\"]}, " +
"{\"names\": \"idx2\", \"privileges\": [\"p3\"], \"fields\": [\"f1\", \"f2\"], \"query\": \"{\\\"match_all\\\": {}}\"}]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
assertEquals("test", rd.getName());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPattern());
assertEquals(3, rd.getIndicesPrivileges().size());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges());
assertEquals(3, rd.getIndicesPrivileges().length);
assertArrayEquals(new String[]{"m", "n"}, rd.getRunAs());
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"], \"indices\": [{\"names\": [\"idx1\",\"idx2\"], " +
"\"privileges\": [\"p1\", \"p2\"]}]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
assertEquals("test", rd.getName());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPattern());
assertEquals(1, rd.getIndicesPrivileges().size());
assertArrayEquals(new String[]{"idx1", "idx2"}, rd.getIndicesPrivileges().get(0).getIndices());
assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges());
assertEquals(1, rd.getIndicesPrivileges().length);
assertArrayEquals(new String[]{"idx1", "idx2"}, rd.getIndicesPrivileges()[0].getIndices());
assertArrayEquals(new String[]{"m", "n"}, rd.getRunAs());
try {
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"], \"indices\": [{\"names\": \"idx1,idx2\", " +
"\"privileges\": [\"p1\", \"p2\"]}]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
fail("should have thrown a parse exception");
} catch (ElasticsearchParseException epe) {
assertTrue(epe.getMessage(),
@ -105,11 +104,26 @@ public class RoleDescriptorTests extends ESTestCase {
// Same, but an array of names
q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"], \"indices\": [{\"names\": [\"idx1,idx2\"], " +
"\"privileges\": [\"p1\", \"p2\"]}]}";
rd = RoleDescriptor.source(new BytesArray(q));
rd = RoleDescriptor.source(null, new BytesArray(q));
fail("should have thrown a parse exception");
} catch (ElasticsearchParseException epe) {
assertTrue(epe.getMessage(),
epe.getMessage().contains("index name [idx1,idx2] may not contain ','"));
}
// Test given name matches the parsed name
q = "{\"name\": \"foo\"}";
rd = RoleDescriptor.source("foo", new BytesArray(q));
assertThat("foo", is(rd.getName()));
try {
// Test mismatch between given name and parsed name
q = "{\"name\": \"foo\"}";
rd = RoleDescriptor.source("bar", new BytesArray(q));
fail("should have thrown a parse exception");
} catch (ElasticsearchParseException epe) {
assertTrue(epe.getMessage(),
epe.getMessage().contains("expected role name [bar] but found [foo] instead"));
}
}
}