diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequest.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequest.java index 71c55d54b51..fc542fb5c16 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequest.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequest.java @@ -19,41 +19,37 @@ import static org.elasticsearch.action.ValidateActions.addValidationError; */ public class DeleteRoleRequest extends ActionRequest { - private String role; + private String name; public DeleteRoleRequest() { } - public DeleteRoleRequest(String roleName) { - this.role = roleName; - } - @Override public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; - if (role == null) { - validationException = addValidationError("role is missing", validationException); + if (name == null) { + validationException = addValidationError("role name is missing", validationException); } return validationException; } - public void role(String role) { - this.role = role; + public void name(String name) { + this.name = name; } - public String role() { - return role; + public String name() { + return name; } @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - role = in.readString(); + name = in.readString(); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeString(role); + out.writeString(name); } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequestBuilder.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequestBuilder.java index 05dad3c2be9..4eec0b7a105 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequestBuilder.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/DeleteRoleRequestBuilder.java @@ -21,8 +21,8 @@ public class DeleteRoleRequestBuilder extends ActionRequestBuilder { - private String[] roles; + private String[] names = Strings.EMPTY_ARRAY; public GetRolesRequest() { - roles = Strings.EMPTY_ARRAY; } @Override public ActionRequestValidationException validate() { ActionRequestValidationException validationException = null; - if (roles == null) { + if (names == null) { validationException = addValidationError("role is missing", validationException); } return validationException; } - public void roles(String... roles) { - this.roles = roles; + public void names(String... names) { + this.names = names; } - public String[] roles() { - return roles; + public String[] names() { + return names; } @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); - roles = in.readStringArray(); + names = in.readStringArray(); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeStringArray(roles); + out.writeStringArray(names); } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/GetRolesRequestBuilder.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/GetRolesRequestBuilder.java index 5889372f67f..83cfcf4a6eb 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/GetRolesRequestBuilder.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/GetRolesRequestBuilder.java @@ -21,8 +21,8 @@ public class GetRolesRequestBuilder extends ActionRequestBuilder roles; - public GetRolesResponse() { - roles = Collections.emptyList(); - } + private RoleDescriptor[] roles; - public GetRolesResponse(RoleDescriptor role) { - this.roles = Collections.singletonList(role); - } - - public GetRolesResponse(List roles) { + public GetRolesResponse(RoleDescriptor... roles) { this.roles = roles; } - public List roles() { + public RoleDescriptor[] roles() { return roles; } - public boolean isExists() { - return roles != null && roles.size() > 0; + public boolean hasRoles() { + return roles.length > 0; } @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); int size = in.readVInt(); - roles = new ArrayList<>(size); + roles = new RoleDescriptor[size]; for (int i = 0; i < size; i++) { - roles.add(RoleDescriptor.readFrom(in)); + roles[i] = RoleDescriptor.readFrom(in); } } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeVInt(roles.size()); + out.writeVInt(roles.length); for (RoleDescriptor role : roles) { RoleDescriptor.writeTo(role, out); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequest.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequest.java index 2f04c19ad7d..339049fc781 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequest.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequest.java @@ -8,11 +8,10 @@ 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; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.shield.authz.RoleDescriptor; import java.io.IOException; @@ -25,13 +24,13 @@ import static org.elasticsearch.action.ValidateActions.addValidationError; /** * Request object for adding a role to the shield index */ -public class PutRoleRequest extends ActionRequest implements ToXContent { +public class PutRoleRequest extends ActionRequest { private String name; - private String[] clusterPrivileges; + private String[] clusterPrivileges = Strings.EMPTY_ARRAY; private List indicesPrivileges = new ArrayList<>(); - private String[] runAs; - + private String[] runAs = Strings.EMPTY_ARRAY; + public PutRoleRequest() { } @@ -44,14 +43,6 @@ public class PutRoleRequest extends ActionRequest 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; } @@ -60,6 +51,10 @@ public class PutRoleRequest extends ActionRequest implements ToX this.clusterPrivileges = clusterPrivileges; } + void addIndex(RoleDescriptor.IndicesPrivileges... privileges) { + this.indicesPrivileges.addAll(Arrays.asList(privileges)); + } + public void addIndex(String[] indices, String[] privileges, @Nullable String[] fields, @Nullable BytesReference query) { this.indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.builder() .indices(indices) @@ -89,38 +84,35 @@ public class PutRoleRequest extends ActionRequest implements ToX return runAs; } - private RoleDescriptor 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(); - clusterPrivileges = in.readOptionalStringArray(); + clusterPrivileges = in.readStringArray(); int indicesSize = in.readVInt(); indicesPrivileges = new ArrayList<>(indicesSize); for (int i = 0; i < indicesSize; i++) { - indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.readIndicesPrivileges(in)); + indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.createFrom(in)); } - runAs = in.readOptionalStringArray(); + runAs = in.readStringArray(); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); out.writeString(name); - out.writeOptionalStringArray(clusterPrivileges); + out.writeStringArray(clusterPrivileges); out.writeVInt(indicesPrivileges.size()); for (RoleDescriptor.IndicesPrivileges index : indicesPrivileges) { index.writeTo(out); } - out.writeOptionalStringArray(runAs); + out.writeStringArray(runAs); } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return this.roleDescriptor().toXContent(builder, params); + RoleDescriptor roleDescriptor() { + return new RoleDescriptor(name, + clusterPrivileges, + indicesPrivileges.toArray(new RoleDescriptor.IndicesPrivileges[indicesPrivileges.size()]), + runAs); } -} +} \ No newline at end of file diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequestBuilder.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequestBuilder.java index 4f3dfdecc26..7c251556cc9 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequestBuilder.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/PutRoleRequestBuilder.java @@ -9,6 +9,7 @@ import org.elasticsearch.action.ActionRequestBuilder; import org.elasticsearch.client.ElasticsearchClient; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.shield.authz.RoleDescriptor; /** * Builder for requests to add a role to the administrative index @@ -24,7 +25,11 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder listener) { try { - rolesStore.removeRole(request, new ActionListener() { + rolesStore.deleteRole(request, new ActionListener() { @Override public void onResponse(Boolean found) { listener.onResponse(new DeleteRoleResponse(found)); diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportGetRolesAction.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportGetRolesAction.java index 91265806263..4f2abde5692 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportGetRolesAction.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportGetRolesAction.java @@ -33,9 +33,9 @@ public class TransportGetRolesAction extends HandledTransportAction listener) { - if (request.roles().length == 1) { - final String rolename = request.roles()[0]; + protected void doExecute(final GetRolesRequest request, final ActionListener listener) { + if (request.names().length == 1) { + final String rolename = request.names()[0]; // We can fetch a single role with a get, much easier rolesStore.getRoleDescriptor(rolename, new ActionListener() { @Override @@ -54,16 +54,16 @@ public class TransportGetRolesAction extends HandledTransportAction>() { + rolesStore.getRoleDescriptors(request.names(), new ActionListener>() { @Override public void onResponse(List roles) { - listener.onResponse(new GetRolesResponse(roles)); + listener.onResponse(new GetRolesResponse(roles.toArray(new RoleDescriptor[roles.size()]))); } @Override public void onFailure(Throwable t) { logger.error("failed to retrieve role [{}]", t, - Strings.arrayToDelimitedString(request.roles(), ",")); + Strings.arrayToDelimitedString(request.names(), ",")); } }); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportPutRoleAction.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportPutRoleAction.java index 3251c8333f9..adc112d18da 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportPutRoleAction.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/role/TransportPutRoleAction.java @@ -28,8 +28,8 @@ public class TransportPutRoleAction extends HandledTransportAction listener) { - rolesStore.addRole(request, new ActionListener() { + protected void doExecute(final PutRoleRequest request, final ActionListener listener) { + rolesStore.putRole(request.roleDescriptor(), new ActionListener() { @Override public void onResponse(Boolean created) { if (created) { diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/RoleDescriptor.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/RoleDescriptor.java index 69bf2df6614..3e90706cf4f 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/RoleDescriptor.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/RoleDescriptor.java @@ -7,6 +7,8 @@ package org.elasticsearch.shield.authz; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; @@ -17,9 +19,11 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.xpack.common.xcontent.XContentUtils; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -33,12 +37,15 @@ public class RoleDescriptor implements ToXContent { private final IndicesPrivileges[] indicesPrivileges; private final String[] runAs; - public RoleDescriptor(String name, String[] clusterPrivileges, - IndicesPrivileges[] indicesPrivileges, String[] runAs) { + public RoleDescriptor(String name, + @Nullable String[] clusterPrivileges, + @Nullable IndicesPrivileges[] indicesPrivileges, + @Nullable String[] runAs) { + this.name = name; - this.clusterPrivileges = clusterPrivileges; - this.indicesPrivileges = indicesPrivileges; - this.runAs = runAs; + this.clusterPrivileges = clusterPrivileges != null ? clusterPrivileges : Strings.EMPTY_ARRAY; + this.indicesPrivileges = indicesPrivileges != null ? indicesPrivileges : IndicesPrivileges.NONE; + this.runAs = runAs != null ? runAs : Strings.EMPTY_ARRAY; } public String getName() { @@ -57,156 +64,6 @@ public class RoleDescriptor implements ToXContent { return this.runAs; } - private static void validateIndexName(String idxName) throws ElasticsearchParseException { - if (idxName == null) { - return; - } - - if (idxName.indexOf(",") != -1) { - throw new ElasticsearchParseException("index name [" + idxName + "] may not contain ','"); - } - } - - private static RoleDescriptor.IndicesPrivileges parseIndex(XContentParser parser) throws Exception { - XContentParser.Token token; - String currentFieldName = null; - String[] idxNames = null; - String query = null; - List privs = new ArrayList<>(); - List fields = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token.isValue()) { - if ("names".equals(currentFieldName)) { - String idxName = parser.text(); - validateIndexName(idxName); - idxNames = new String[]{idxName}; - } else if ("query".equals(currentFieldName)) { - query = parser.text(); - } else { - throw new ElasticsearchParseException("unexpected field in add role request [{}]", currentFieldName); - } - } else if (token == XContentParser.Token.START_OBJECT) { - // expected - } else if (token == XContentParser.Token.START_ARRAY && "names".equals(currentFieldName)) { - List idxNameList = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token.isValue()) { - String idxName = parser.text(); - validateIndexName(idxName); - idxNameList.add(idxName); - } else { - throw new ElasticsearchParseException("unexpected object while parsing index names [{}]", token); - } - } - idxNames = idxNameList.toArray(Strings.EMPTY_ARRAY); - } else if (token == XContentParser.Token.START_ARRAY && "privileges".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token.isValue()) { - privs.add(parser.text()); - } else { - throw new ElasticsearchParseException("unexpected object while parsing index privileges [{}]", token); - } - } - } else if (token == XContentParser.Token.START_ARRAY && "fields".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token.isValue()) { - fields.add(parser.text()); - } else { - throw new ElasticsearchParseException("unexpected object while parsing index fields [{}]", token); - } - } - } else { - throw new ElasticsearchParseException("failed to parse add role request indices, got value with wrong type [{}]", - currentFieldName); - } - } - if (idxNames == null || idxNames.length == 0) { - throw new ElasticsearchParseException("'name' is a required field for index permissions"); - } - if (privs.isEmpty()) { - throw new ElasticsearchParseException("'privileges' is a required field for index permissions"); - } - return RoleDescriptor.IndicesPrivileges.builder() - .indices(idxNames) - .privileges(privs.toArray(Strings.EMPTY_ARRAY)) - .fields(fields.isEmpty() ? null : fields.toArray(Strings.EMPTY_ARRAY)) - .query(query == null ? null : new BytesArray(query)) - .build(); - } - - private static List parseIndices(XContentParser parser) throws Exception { - XContentParser.Token token; - List tempIndices = new ArrayList<>(); - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token == XContentParser.Token.START_OBJECT) { - tempIndices.add(parseIndex(parser)); - } else { - throw new ElasticsearchParseException("unexpected type parsing index sub object [{}]", token); - } - } - return tempIndices; - } - - public static RoleDescriptor source(String name, BytesReference source) throws Exception { - try (XContentParser parser = XContentHelper.createParser(source)) { - XContentParser.Token token; - String currentFieldName = null; - String roleName = name; - List indicesPrivileges = new ArrayList<>(); - List runAsUsers = new ArrayList<>(); - List 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)) { - 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); - } - } else if (token == XContentParser.Token.START_ARRAY && "indices".equals(currentFieldName)) { - indicesPrivileges = parseIndices(parser); - } else if (token == XContentParser.Token.START_ARRAY && "run_as".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token.isValue()) { - runAsUsers.add(parser.text()); - } else { - throw new ElasticsearchParseException("unexpected value parsing run_as users [{}]", token); - } - } - } else if (token == XContentParser.Token.START_ARRAY && "cluster".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (token.isValue()) { - clusterPrivileges.add(parser.text()); - } else { - throw new ElasticsearchParseException("unexpected value parsing cluster privileges [{}]", token); - } - } - } else { - throw new ElasticsearchParseException("failed to parse add role request, got value with wrong type [{}]", - currentFieldName); - } - } - if (roleName == null) { - throw new ElasticsearchParseException("field [name] required for role description"); - } - return new RoleDescriptor(roleName, clusterPrivileges.toArray(new String[clusterPrivileges.size()]), - indicesPrivileges.toArray(new IndicesPrivileges[indicesPrivileges.size()]), - runAsUsers.toArray(new String[runAsUsers.size()])); - } - } - @Override public String toString() { StringBuilder sb = new StringBuilder("Role["); @@ -222,30 +79,47 @@ public class RoleDescriptor implements ToXContent { } @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RoleDescriptor that = (RoleDescriptor) o; + + if (!name.equals(that.name)) return false; + if (!Arrays.equals(clusterPrivileges, that.clusterPrivileges)) return false; + if (!Arrays.equals(indicesPrivileges, that.indicesPrivileges)) return false; + return Arrays.equals(runAs, that.runAs); + } + + @Override + public int hashCode() { + int result = name.hashCode(); + result = 31 * result + Arrays.hashCode(clusterPrivileges); + result = 31 * result + Arrays.hashCode(indicesPrivileges); + result = 31 * result + Arrays.hashCode(runAs); + return result; + } + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - builder.field("name", name); - builder.field("cluster", clusterPrivileges); + builder.field("cluster", (Object[]) clusterPrivileges); builder.field("indices", (Object[]) indicesPrivileges); if (runAs != null) { builder.field("run_as", runAs); } - builder.endObject(); - return builder; + return builder.endObject(); } public static RoleDescriptor readFrom(StreamInput in) throws IOException { String name = in.readString(); - String[] clusterPattern = in.readStringArray(); + String[] clusterPrivileges = in.readStringArray(); int size = in.readVInt(); - List indicesPrivileges = new ArrayList<>(size); + IndicesPrivileges[] indicesPrivileges = new IndicesPrivileges[size]; for (int i = 0; i < size; i++) { - IndicesPrivileges group = new IndicesPrivileges(); - group.readFrom(in); - indicesPrivileges.add(group); + indicesPrivileges[i] = IndicesPrivileges.createFrom(in); } String[] runAs = in.readStringArray(); - return new RoleDescriptor(name, clusterPattern, indicesPrivileges.toArray(new IndicesPrivileges[indicesPrivileges.size()]), runAs); + return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAs); } public static void writeTo(RoleDescriptor descriptor, StreamOutput out) throws IOException { @@ -258,38 +132,103 @@ public class RoleDescriptor implements ToXContent { out.writeStringArray(descriptor.runAs); } - public static class IndicesPrivilegesBuilder { - private String[] privileges; - private String[] indices; - private String[] fields; - private BytesReference query; - - IndicesPrivilegesBuilder() { + public static RoleDescriptor parse(String name, BytesReference source) throws Exception { + assert name != null; + try (XContentParser parser = XContentHelper.createParser(source)) { + XContentParser.Token token = parser.nextToken(); // advancing to the START_OBJECT token + 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; + String[] clusterPrivileges = null; + String[] runAsUsers = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.INDICES)) { + indicesPrivileges = parseIndices(name, parser); + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.RUN_AS)) { + runAsUsers = XContentUtils.readStringArray(parser, true); + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.CLUSTER)) { + clusterPrivileges = XContentUtils.readStringArray(parser, true); + } else { + throw new ElasticsearchParseException("failed to parse role [{}]. unexpected field [{}]", name, currentFieldName); + } + } + return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, runAsUsers); } + } - public IndicesPrivilegesBuilder indices(String[] indices) { - this.indices = indices; - return this; + private static RoleDescriptor.IndicesPrivileges[] parseIndices(String roleName, XContentParser parser) throws Exception { + if (parser.currentToken() != XContentParser.Token.START_ARRAY) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. expected field [{}] value " + + "to be an array, but found [{}] instead", roleName, parser.currentName(), parser.currentToken()); } + List privileges = new ArrayList<>(); + XContentParser.Token token; + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + privileges.add(parseIndex(roleName, parser)); + } + return privileges.toArray(new IndicesPrivileges[privileges.size()]); + } - public IndicesPrivilegesBuilder privileges(String[] privileges) { - this.privileges = privileges; - return this; + private static RoleDescriptor.IndicesPrivileges parseIndex(String roleName, XContentParser parser) throws Exception { + XContentParser.Token token = parser.currentToken(); + if (token != XContentParser.Token.START_OBJECT) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. expected field [{}] value to " + + "be an array of objects, but found an array element of type [{}]", roleName, parser.currentName(), token); } - - public IndicesPrivilegesBuilder fields(@Nullable String[] fields) { - this.fields = fields; - return this; + String currentFieldName = null; + String[] names = null; + String query = null; + String[] privileges = null; + String[] fields = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.NAMES)) { + if (token == XContentParser.Token.VALUE_STRING) { + names = new String[] { parser.text() }; + } else if (token == XContentParser.Token.START_ARRAY) { + names = XContentUtils.readStringArray(parser, false); + if (names.length == 0) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. [{}] cannot be an empty " + + "array", roleName, currentFieldName); + } + } else { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. expected field [{}] " + + "value to be a string or an array of strings, but found [{}] instead", roleName, currentFieldName, token); + } + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.QUERY)) { + query = parser.textOrNull(); + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.PRIVILEGES)) { + privileges = XContentUtils.readStringArray(parser, false); + if (names.length == 0) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. [{}] cannot be an empty " + + "array", roleName, currentFieldName); + } + } else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.FIELDS)) { + fields = XContentUtils.readStringArray(parser, true); + } else { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. unexpected field [{}]", + roleName, currentFieldName); + } } - - public IndicesPrivilegesBuilder query(@Nullable BytesReference query) { - this.query = query; - return this; + if (names == null) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. missing required [{}] field", + roleName, Fields.NAMES.getPreferredName()); } - - public IndicesPrivileges build() { - return new IndicesPrivileges(privileges, indices, fields, query); + if (privileges == null) { + throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. missing required [{}] field", + roleName, Fields.PRIVILEGES.getPreferredName()); } + return RoleDescriptor.IndicesPrivileges.builder() + .indices(names) + .privileges(privileges) + .fields(fields) + .query(query) + .build(); } /** @@ -298,34 +237,28 @@ public class RoleDescriptor implements ToXContent { */ public static class IndicesPrivileges implements ToXContent, Streamable { - private String[] privileges; + private static final IndicesPrivileges[] NONE = new IndicesPrivileges[0]; + private String[] indices; + private String[] privileges; private String[] fields; private BytesReference query; private IndicesPrivileges() { } - IndicesPrivileges(String[] privileges, String[] indices, - @Nullable String[] fields, @Nullable BytesReference query) { - this.privileges = privileges; - this.indices = indices; - this.fields = fields; - this.query = query; - } - - public static IndicesPrivilegesBuilder builder() { - return new IndicesPrivilegesBuilder(); - } - - public String[] getPrivileges() { - return this.privileges; + public static Builder builder() { + return new Builder(); } public String[] getIndices() { return this.indices; } + public String[] getPrivileges() { + return this.privileges; + } + @Nullable public String[] getFields() { return this.fields; @@ -339,8 +272,8 @@ public class RoleDescriptor implements ToXContent { @Override public String toString() { StringBuilder sb = new StringBuilder("IndicesPrivileges["); - sb.append("privileges=[").append(Strings.arrayToCommaDelimitedString(privileges)); - sb.append("], indices=[").append(Strings.arrayToCommaDelimitedString(indices)); + sb.append("indices=[").append(Strings.arrayToCommaDelimitedString(indices)); + sb.append("], privileges=[").append(Strings.arrayToCommaDelimitedString(privileges)); sb.append("], fields=[").append(Strings.arrayToCommaDelimitedString(fields)); if (query != null) { sb.append("], query=").append(query.toUtf8()); @@ -349,9 +282,31 @@ public class RoleDescriptor implements ToXContent { return sb.toString(); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + IndicesPrivileges that = (IndicesPrivileges) o; + + if (!Arrays.equals(indices, that.indices)) return false; + if (!Arrays.equals(privileges, that.privileges)) return false; + if (!Arrays.equals(fields, that.fields)) return false; + return !(query != null ? !query.equals(that.query) : that.query != null); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(indices); + result = 31 * result + Arrays.hashCode(privileges); + result = 31 * result + Arrays.hashCode(fields); + result = 31 * result + (query != null ? query.hashCode() : 0); + return result; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); // start + builder.startObject(); builder.array("names", indices); builder.array("privileges", privileges); if (fields != null) { @@ -360,11 +315,10 @@ public class RoleDescriptor implements ToXContent { if (query != null) { builder.field("query", query.toUtf8()); } - builder.endObject(); // end start - return builder; + return builder.endObject(); } - public static IndicesPrivileges readIndicesPrivileges(StreamInput in) throws IOException { + public static IndicesPrivileges createFrom(StreamInput in) throws IOException { IndicesPrivileges ip = new IndicesPrivileges(); ip.readFrom(in); return ip; @@ -372,9 +326,9 @@ public class RoleDescriptor implements ToXContent { @Override public void readFrom(StreamInput in) throws IOException { - this.privileges = in.readStringArray(); this.indices = in.readStringArray(); this.fields = in.readOptionalStringArray(); + this.privileges = in.readStringArray(); if (in.readBoolean()) { this.query = new BytesArray(in.readByteArray()); } @@ -382,9 +336,9 @@ public class RoleDescriptor implements ToXContent { @Override public void writeTo(StreamOutput out) throws IOException { - out.writeStringArray(privileges); out.writeStringArray(indices); out.writeOptionalStringArray(fields); + out.writeStringArray(privileges); if (query != null) { out.writeBoolean(true); out.writeByteArray(query.array()); @@ -392,5 +346,57 @@ public class RoleDescriptor implements ToXContent { out.writeBoolean(false); } } + + public static class Builder { + + private IndicesPrivileges indicesPrivileges = new IndicesPrivileges(); + + private Builder() { + } + + public Builder indices(String... indices) { + indicesPrivileges.indices = indices; + return this; + } + + public Builder privileges(String... privileges) { + indicesPrivileges.privileges = privileges; + return this; + } + + public Builder fields(@Nullable String... fields) { + indicesPrivileges.fields = fields; + return this; + } + + public Builder query(@Nullable String query) { + return query(query == null ? null : new BytesArray(query)); + } + + public Builder query(@Nullable BytesReference query) { + indicesPrivileges.query = query; + return this; + } + + public IndicesPrivileges build() { + if (indicesPrivileges.indices == null || indicesPrivileges.indices.length == 0) { + throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern"); + } + if (indicesPrivileges.privileges == null || indicesPrivileges.privileges.length == 0) { + throw new IllegalArgumentException("indices privileges must define at least one privilege"); + } + return indicesPrivileges; + } + } + } + + public interface Fields { + ParseField CLUSTER = new ParseField("cluster"); + ParseField INDICES = new ParseField("indices"); + ParseField RUN_AS = new ParseField("run_as"); + ParseField NAMES = new ParseField("names"); + ParseField QUERY = new ParseField("query"); + ParseField PRIVILEGES = new ParseField("privileges"); + ParseField FIELDS = new ParseField("fields"); } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java index 6ea964fab91..ece071259e7 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java @@ -12,7 +12,6 @@ import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.delete.DeleteResponse; import org.elasticsearch.action.get.GetRequest; import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.action.search.ClearScrollRequest; import org.elasticsearch.action.search.ClearScrollResponse; @@ -40,7 +39,6 @@ import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.shield.InternalClient; import org.elasticsearch.shield.ShieldTemplateService; -import org.elasticsearch.shield.action.role.PutRoleRequest; import org.elasticsearch.shield.action.role.ClearRolesCacheRequest; import org.elasticsearch.shield.action.role.ClearRolesCacheResponse; import org.elasticsearch.shield.action.role.DeleteRoleRequest; @@ -76,7 +74,16 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; */ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, ClusterStateListener { - public static final String INDEX_ROLE_TYPE = "role"; + public enum State { + INITIALIZED, + STARTING, + STARTED, + STOPPING, + STOPPED, + FAILED + } + + public static final String ROLE_DOC_TYPE = "role"; private final Provider clientProvider; private final ThreadPool threadPool; @@ -99,28 +106,66 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, this.threadPool = threadPool; } - @Nullable - private RoleDescriptor transformRole(GetResponse response) { - if (response.isExists() == false) { - return null; + public boolean canStart(ClusterState clusterState, boolean master) { + if (state() != ESNativeRolesStore.State.INITIALIZED) { + return false; } - return transformRole(response.getSourceAsBytesRef()); + + if (clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { + // wait until the gateway has recovered from disk, otherwise we + // think may not have the .shield index but they it may not have + // been restored from the cluster state on disk yet + logger.debug("native roles store waiting until gateway has recovered from disk"); + return false; + } + + if (clusterState.metaData().templates().get(ShieldTemplateService.SECURITY_TEMPLATE_NAME) == null) { + logger.debug("native roles template [{}] does not exist, so service cannot start", + ShieldTemplateService.SECURITY_TEMPLATE_NAME); + return false; + } + // Okay to start... + return true; } - @Nullable - private RoleDescriptor transformRole(BytesReference sourceBytes) { + public void start() { try { - return RoleDescriptor.source(null, sourceBytes); + if (state.compareAndSet(State.INITIALIZED, State.STARTING)) { + this.client = clientProvider.get(); + this.securityClient = new SecurityClient(client); + this.scrollSize = settings.getAsInt("shield.authc.native.scroll.size", 1000); + this.scrollKeepAlive = settings.getAsTime("shield.authc.native.scroll.keep_alive", TimeValue.timeValueSeconds(10L)); + TimeValue pollInterval = settings.getAsTime("shield.authc.native.reload.interval", TimeValue.timeValueSeconds(30L)); + RolesStorePoller poller = new RolesStorePoller(); + try { + poller.doRun(); + } catch (Exception e) { + logger.warn("failed to perform initial poll of roles index [{}]. scheduling again in [{}]", e, + ShieldTemplateService.SECURITY_INDEX_NAME, pollInterval); + } + versionChecker = threadPool.scheduleWithFixedDelay(poller, pollInterval); + state.set(State.STARTED); + } } catch (Exception e) { - logger.warn("unable to deserialize role from response", e); - return null; + logger.error("failed to start ESNativeRolesStore", e); + state.set(State.FAILED); + } + } + + public void stop() { + if (state.compareAndSet(State.STARTED, State.STOPPING)) { + try { + FutureUtils.cancel(versionChecker); + } finally { + state.set(State.STOPPED); + } } } /** * Retrieve a list of roles, if rolesToGet is null or empty, fetch all roles */ - public void getRoleDescriptors(String[] rolesToGet, final ActionListener> listener) { + public void getRoleDescriptors(String[] names, final ActionListener> listener) { if (state() != State.STARTED) { logger.trace("attempted to get roles before service was started"); listener.onFailure(new IllegalStateException("roles cannot be retrieved as native role service has not been started")); @@ -129,12 +174,13 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, try { final List roles = new ArrayList<>(); QueryBuilder query; - if (rolesToGet == null || rolesToGet.length == 0) { - query = QueryBuilders.boolQuery().filter(QueryBuilders.termQuery("_type", INDEX_ROLE_TYPE)); + if (names == null || names.length == 0) { + query = QueryBuilders.matchAllQuery(); } else { - query = QueryBuilders.boolQuery().filter(QueryBuilders.idsQuery(INDEX_ROLE_TYPE).addIds(rolesToGet)); + query = QueryBuilders.boolQuery().filter(QueryBuilders.idsQuery(ROLE_DOC_TYPE).addIds(names)); } SearchRequest request = client.prepareSearch(ShieldTemplateService.SECURITY_INDEX_NAME) + .setTypes(ROLE_DOC_TYPE) .setScroll(scrollKeepAlive) .setQuery(query) .setSize(scrollSize) @@ -149,7 +195,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, boolean hasHits = resp.getHits().getHits().length > 0; if (hasHits) { for (SearchHit hit : resp.getHits().getHits()) { - RoleDescriptor rd = transformRole(hit.getSourceRef()); + RoleDescriptor rd = transformRole(hit.getId(), hit.getSourceRef()); if (rd != null) { roles.add(rd); } @@ -203,30 +249,19 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, listener.onResponse(roleAndVersion == null ? null : roleAndVersion.getRoleDescriptor()); } - private void executeGetRoleRequest(String role, ActionListener listener) { - try { - GetRequest request = client.prepareGet(ShieldTemplateService.SECURITY_INDEX_NAME, INDEX_ROLE_TYPE, role).request(); - request.indicesOptions().ignoreUnavailable(); - client.get(request, listener); - } catch (Exception e) { - logger.error("unable to retrieve role", e); - listener.onFailure(e); - } - } - - public void removeRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener listener) { + public void deleteRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener listener) { if (state() != State.STARTED) { - logger.trace("attempted to delete role [{}] before service was started", deleteRoleRequest.role()); + logger.trace("attempted to delete role [{}] before service was started", deleteRoleRequest.name()); listener.onResponse(false); } try { DeleteRequest request = client.prepareDelete(ShieldTemplateService.SECURITY_INDEX_NAME, - INDEX_ROLE_TYPE, deleteRoleRequest.role()).request(); + ROLE_DOC_TYPE, deleteRoleRequest.name()).request(); request.indicesOptions().ignoreUnavailable(); client.delete(request, new ActionListener() { @Override public void onResponse(DeleteResponse deleteResponse) { - clearRoleCache(deleteRoleRequest.role(), listener, deleteResponse.isFound()); + clearRoleCache(deleteRoleRequest.name(), listener, deleteResponse.isFound()); } @Override @@ -241,7 +276,44 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, } } - private RoleAndVersion getRoleAndVersion(String roleId) { + public void putRole(final RoleDescriptor role, final ActionListener listener) { + if (state() != State.STARTED) { + logger.trace("attempted to put role before service was started"); + listener.onResponse(false); + } + try { + client.prepareIndex(ShieldTemplateService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE, role.getName()) + .setSource(role.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) + .execute(new ActionListener() { + @Override + public void onResponse(IndexResponse indexResponse) { + if (indexResponse.isCreated()) { + listener.onResponse(indexResponse.isCreated()); + return; + } + clearRoleCache(role.getName(), listener, indexResponse.isCreated()); + } + + @Override + public void onFailure(Throwable e) { + logger.error("failed to put role to the index", e); + listener.onFailure(e); + } + }); + } catch (Exception e) { + logger.error("unable to put role", e); + listener.onFailure(e); + } + + } + + @Override + public Role role(String roleName) { + RoleAndVersion roleAndVersion = getRoleAndVersion(roleName); + return roleAndVersion == null ? null : roleAndVersion.getRole(); + } + + private RoleAndVersion getRoleAndVersion(final String roleId) { RoleAndVersion roleAndVersion = null; final AtomicReference getRef = new AtomicReference<>(null); final CountDownLatch latch = new CountDownLatch(1); @@ -288,99 +360,15 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, return roleAndVersion; } - public void addRole(final PutRoleRequest putRoleRequest, final ActionListener listener) { - if (state() != State.STARTED) { - logger.trace("attempted to add role before service was started"); - listener.onResponse(false); - } + private void executeGetRoleRequest(String role, ActionListener listener) { try { - IndexRequest request = client.prepareIndex(ShieldTemplateService.SECURITY_INDEX_NAME, - INDEX_ROLE_TYPE, putRoleRequest.name()) - .setSource(putRoleRequest.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS)) - .request(); - client.index(request, new ActionListener() { - @Override - public void onResponse(IndexResponse indexResponse) { - if (indexResponse.isCreated()) { - listener.onResponse(indexResponse.isCreated()); - return; - } - clearRoleCache(putRoleRequest.name(), listener, indexResponse.isCreated()); - } - - @Override - public void onFailure(Throwable e) { - logger.error("failed to add role to the index", e); - listener.onFailure(e); - } - }); + GetRequest request = client.prepareGet(ShieldTemplateService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE, role).request(); + request.indicesOptions().ignoreUnavailable(); + client.get(request, listener); } catch (Exception e) { - logger.error("unable to add role", e); + logger.error("unable to retrieve role", e); listener.onFailure(e); } - - } - - @Override - public Role role(String roleName) { - RoleAndVersion roleAndVersion = getRoleAndVersion(roleName); - return roleAndVersion == null ? null : roleAndVersion.getRole(); - } - - public boolean canStart(ClusterState clusterState, boolean master) { - if (state() != ESNativeRolesStore.State.INITIALIZED) { - return false; - } - - if (clusterState.blocks().hasGlobalBlock(GatewayService.STATE_NOT_RECOVERED_BLOCK)) { - // wait until the gateway has recovered from disk, otherwise we - // think may not have the .security index but they it may not have - // been restored from the cluster state on disk yet - logger.debug("native roles store waiting until gateway has recovered from disk"); - return false; - } - - if (clusterState.metaData().templates().get(ShieldTemplateService.SECURITY_TEMPLATE_NAME) == null) { - logger.debug("native roles template [{}] does not exist, so service cannot start", - ShieldTemplateService.SECURITY_TEMPLATE_NAME); - return false; - } - // Okay to start... - return true; - } - - public void start() { - try { - if (state.compareAndSet(State.INITIALIZED, State.STARTING)) { - this.client = clientProvider.get(); - this.securityClient = new SecurityClient(client); - this.scrollSize = settings.getAsInt("shield.authc.native.scroll.size", 1000); - this.scrollKeepAlive = settings.getAsTime("shield.authc.native.scroll.keep_alive", TimeValue.timeValueSeconds(10L)); - TimeValue pollInterval = settings.getAsTime("shield.authc.native.reload.interval", TimeValue.timeValueSeconds(30L)); - RolesStorePoller poller = new RolesStorePoller(); - try { - poller.doRun(); - } catch (Exception e) { - logger.warn("failed to perform initial poll of roles index [{}]. scheduling again in [{}]", e, - ShieldTemplateService.SECURITY_INDEX_NAME, pollInterval); - } - versionChecker = threadPool.scheduleWithFixedDelay(poller, pollInterval); - state.set(State.STARTED); - } - } catch (Exception e) { - logger.error("failed to start ESNativeRolesStore", e); - state.set(State.FAILED); - } - } - - public void stop() { - if (state.compareAndSet(State.STARTED, State.STOPPING)) { - try { - FutureUtils.cancel(versionChecker); - } finally { - state.set(State.STOPPED); - } - } } // FIXME hack for testing @@ -442,13 +430,22 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, return state.get(); } - public enum State { - INITIALIZED, - STARTING, - STARTED, - STOPPING, - STOPPED, - FAILED + @Nullable + private RoleDescriptor transformRole(GetResponse response) { + if (response.isExists() == false) { + return null; + } + return transformRole(response.getId(), response.getSourceAsBytesRef()); + } + + @Nullable + private RoleDescriptor transformRole(String name, BytesReference sourceBytes) { + try { + return RoleDescriptor.parse(name, sourceBytes); + } catch (Exception e) { + logger.warn("unable to deserialize role from response", e); + return null; + } } private class RolesStorePoller extends AbstractRunnable { @@ -475,7 +472,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, try { SearchRequest request = client.prepareSearch(ShieldTemplateService.SECURITY_INDEX_NAME) .setScroll(scrollKeepAlive) - .setQuery(QueryBuilders.typeQuery(INDEX_ROLE_TYPE)) + .setQuery(QueryBuilders.typeQuery(ROLE_DOC_TYPE)) .setSize(scrollSize) .setFetchSource(true) .setVersion(true) @@ -487,7 +484,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, if (isStopped()) { return; } - for (final SearchHit hit : response.getHits().getHits()) { + for (SearchHit hit : response.getHits().getHits()) { final String roleName = hit.getId(); final long version = hit.version(); existingRoles.remove(roleName); @@ -497,7 +494,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, @Override public RoleAndVersion apply(String roleName, RoleAndVersion existing) { if (version > existing.getVersion()) { - RoleDescriptor rd = transformRole(hit.getSourceRef()); + RoleDescriptor rd = transformRole(hit.getId(), hit.getSourceRef()); if (rd != null) { return new RoleAndVersion(rd, version); } @@ -541,6 +538,7 @@ public class ESNativeRolesStore extends AbstractComponent implements RolesStore, } private static class RoleAndVersion { + private final RoleDescriptor roleDescriptor; private final Role role; private final long version; diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/role/RestGetRolesAction.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/role/RestGetRolesAction.java index 7b29552af0e..dc288a2ddc7 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/role/RestGetRolesAction.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/rest/action/role/RestGetRolesAction.java @@ -9,7 +9,6 @@ import org.elasticsearch.client.Client; import org.elasticsearch.common.Strings; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.BytesRestResponse; @@ -21,6 +20,7 @@ import org.elasticsearch.rest.RestStatus; import org.elasticsearch.rest.action.support.RestBuilderListener; import org.elasticsearch.shield.action.role.GetRolesResponse; import org.elasticsearch.shield.client.SecurityClient; +import org.elasticsearch.shield.authz.RoleDescriptor; /** * Rest endpoint to retrieve a Role from the shield index @@ -35,20 +35,25 @@ public class RestGetRolesAction extends BaseRestHandler { } @Override - protected void handleRequest(RestRequest request, final RestChannel channel, Client client) throws Exception { - String[] names = request.paramAsStringArrayOrEmptyIfAll("name"); - - new SecurityClient(client).prepareGetRoles(names).execute(new RestBuilderListener(channel) { + protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception { + final String[] roles = request.paramAsStringArray("name", Strings.EMPTY_ARRAY); + new SecurityClient(client).prepareGetRoles(roles).execute(new RestBuilderListener(channel) { @Override - public RestResponse buildResponse(GetRolesResponse getRolesResponse, XContentBuilder builder) throws Exception { + public RestResponse buildResponse(GetRolesResponse response, XContentBuilder builder) throws Exception { builder.startObject(); - builder.field("found", getRolesResponse.isExists()); - builder.startArray("roles"); - for (ToXContent role : getRolesResponse.roles()) { - role.toXContent(builder, ToXContent.EMPTY_PARAMS); + for (RoleDescriptor role : response.roles()) { + builder.field(role.getName(), role); } - builder.endArray(); builder.endObject(); + + // if the user asked for specific roles, but none of them were found + // we'll return an empty result and 404 status code + if (roles.length != 0 && response.roles().length == 0) { + return new BytesRestResponse(RestStatus.NOT_FOUND, builder); + } + + // either the user asked for all roles, or at least one of the roles + // the user asked for was found return new BytesRestResponse(RestStatus.OK, builder); } }); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java index f4629fe0ef6..0626330c0a8 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java @@ -33,6 +33,7 @@ import org.junit.BeforeClass; import java.util.Arrays; import java.util.List; +import static org.hamcrest.Matchers.arrayWithSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.notNullValue; @@ -135,7 +136,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { logger.debug("--> modifying roles {} to have run_as", toModify); for (String role : toModify) { UpdateResponse response = client.prepareUpdate().setId(role).setIndex(ShieldTemplateService.SECURITY_INDEX_NAME) - .setType(ESNativeRolesStore.INDEX_ROLE_TYPE) + .setType(ESNativeRolesStore.ROLE_DOC_TYPE) .setDoc("run_as", new String[] { role }) .get(); assertThat(response.isCreated(), is(false)); @@ -174,17 +175,17 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { SecurityClient securityClient = securityClient(client); final String role = randomFrom(roles); - List foundRoles = securityClient.prepareGetRoles().names(role).get().roles(); - assertThat(foundRoles.size(), is(1)); + RoleDescriptor[] foundRoles = securityClient.prepareGetRoles().names(role).get().roles(); + assertThat(foundRoles.length, is(1)); logger.debug("--> deleting role [{}]", role); - DeleteResponse response = client.prepareDelete(ShieldTemplateService.SECURITY_INDEX_NAME, - ESNativeRolesStore.INDEX_ROLE_TYPE, role).get(); + DeleteResponse response = client + .prepareDelete(ShieldTemplateService.SECURITY_INDEX_NAME, ESNativeRolesStore.ROLE_DOC_TYPE, role).get(); assertThat(response.isFound(), is(true)); assertBusy(new Runnable() { @Override public void run() { - assertThat(securityClient.prepareGetRoles().names(role).get().roles().isEmpty(), is(true)); + assertThat(securityClient.prepareGetRoles().names(role).get().roles(), arrayWithSize(0)); } }); } @@ -193,8 +194,8 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { for (String role : roles) { logger.debug("--> getting role [{}]", role); GetRolesResponse roleResponse = securityClient.prepareGetRoles().names(role).get(); - assertThat(roleResponse.isExists(), is(true)); - final String[] runAs = roleResponse.roles().get(0).getRunAs(); + assertThat(roleResponse.hasRoles(), is(true)); + final String[] runAs = roleResponse.roles()[0].getRunAs(); if (toModify.contains(role)) { assertThat("role [" + role + "] should be modified and have run as", runAs == null || runAs.length == 0, is(false)); assertThat(Arrays.asList(runAs).contains(role), is(true)); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/ESNativeTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/ESNativeTests.java index 3439ea73387..fa6158660e5 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/ESNativeTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/esnative/ESNativeTests.java @@ -55,7 +55,7 @@ public class ESNativeTests extends ShieldIntegTestCase { GetUsersResponse resp = c.prepareGetUsers("joe").get(); assertFalse("user should not exist", resp.hasUsers()); GetRolesResponse resp2 = c.prepareGetRoles().names("role").get(); - assertFalse("role should not exist", resp2.isExists()); + assertFalse("role should not exist", resp2.hasRoles()); } public void testAddAndGetUser() throws Exception { @@ -117,8 +117,8 @@ public class ESNativeTests extends ShieldIntegTestCase { ensureGreen(ShieldTemplateService.SECURITY_INDEX_NAME); logger.info("--> retrieving role"); GetRolesResponse resp = c.prepareGetRoles().names("test_role").get(); - assertTrue("role should exist", resp.isExists()); - RoleDescriptor testRole = resp.roles().get(0); + assertTrue("role should exist", resp.hasRoles()); + RoleDescriptor testRole = resp.roles()[0]; assertNotNull(testRole); c.preparePutRole("test_role2") @@ -139,20 +139,20 @@ public class ESNativeTests extends ShieldIntegTestCase { logger.info("--> retrieving all roles"); GetRolesResponse allRolesResp = c.prepareGetRoles().get(); - assertTrue("roles should exist", allRolesResp.isExists()); - assertEquals("should be 3 roles total", 3, allRolesResp.roles().size()); + assertTrue("roles should exist", allRolesResp.hasRoles()); + assertEquals("should be 3 roles total", 3, allRolesResp.roles().length); logger.info("--> retrieving all roles"); GetRolesResponse someRolesResp = c.prepareGetRoles().names("test_role", "test_role3").get(); - assertTrue("roles should exist", someRolesResp.isExists()); - assertEquals("should be 2 roles total", 2, someRolesResp.roles().size()); + assertTrue("roles should exist", someRolesResp.hasRoles()); + assertEquals("should be 2 roles total", 2, someRolesResp.roles().length); logger.info("--> deleting role"); DeleteRoleResponse delResp = c.prepareDeleteRole("test_role").get(); assertTrue(delResp.found()); logger.info("--> retrieving role"); GetRolesResponse resp2 = c.prepareGetRoles().names("test_role").get(); - assertFalse("role should not exist after being deleted", resp2.isExists()); + assertFalse("role should not exist after being deleted", resp2.hasRoles()); } public void testAddUserAndRoleThenAuth() throws Exception { @@ -160,8 +160,8 @@ public class ESNativeTests extends ShieldIntegTestCase { logger.error("--> creating role"); c.preparePutRole("test_role") .cluster("all") - .addIndices(new String[]{"*"}, new String[]{"read"}, - new String[]{"body", "title"}, new BytesArray("{\"match_all\": {}}")) + .addIndices(new String[] { "*" }, new String[] { "read" }, + new String[] { "body", "title" }, new BytesArray("{\"match_all\": {}}")) .get(); logger.error("--> creating user"); c.preparePutUser("joe", "s3krit".toCharArray(), "test_role").get(); @@ -284,19 +284,20 @@ public class ESNativeTests extends ShieldIntegTestCase { } } else { GetRolesResponse getRolesResponse = c.prepareGetRoles().names("test_role").get(); - assertTrue("test_role does not exist!", getRolesResponse.isExists()); + assertTrue("test_role does not exist!", getRolesResponse.hasRoles()); assertTrue("any cluster permission should be authorized", - Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/foo")); + Role.builder(getRolesResponse.roles()[0]).build().cluster().check("cluster:admin/foo")); + c.preparePutRole("test_role") .cluster("none") .addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, new BytesArray("{\"match_all\": {}}")) .get(); getRolesResponse = c.prepareGetRoles().names("test_role").get(); - assertTrue("test_role does not exist!", getRolesResponse.isExists()); + assertTrue("test_role does not exist!", getRolesResponse.hasRoles()); assertFalse("no cluster permission should be authorized", - Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/bar")); + Role.builder(getRolesResponse.roles()[0]).build().cluster().check("cluster:admin/bar")); } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/RoleDescriptorTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/RoleDescriptorTests.java index 82b3e0c1788..d7e157e3fe9 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/RoleDescriptorTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/RoleDescriptorTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.shield.authz; -import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.xcontent.ToXContent; @@ -19,111 +18,75 @@ public class RoleDescriptorTests extends ESTestCase { public void testIndexGroup() throws Exception { RoleDescriptor.IndicesPrivileges privs = RoleDescriptor.IndicesPrivileges.builder() - .indices(new String[]{"idx"}) - .privileges(new String[]{"priv"}) + .indices("idx") + .privileges("priv") .build(); XContentBuilder b = jsonBuilder(); privs.toXContent(b, ToXContent.EMPTY_PARAMS); assertEquals("{\"names\":[\"idx\"],\"privileges\":[\"priv\"]}", b.string()); } - public void testRDJson() throws Exception { + public void testToString() throws Exception { 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\": {}}}")) + .indices("i1", "i2") + .privileges("read") + .fields("body", "title") + .query("{\"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()); - XContentBuilder builder = jsonBuilder(); - d.toXContent(builder, ToXContent.EMPTY_PARAMS); - assertEquals("{\"name\":\"test\",\"cluster\":[\"all\",\"none\"],\"indices\":[{\"names\":[\"i1\",\"i2\"]," + - "\"privileges\":[\"read\"],\"fields\":[\"body\",\"title\"],\"query\":\"{\\\"query\\\": {\\\"match_all\\\": {}}}\"}]," + - "\"run_as\":[\"sudo\"]}", - builder.string()); + RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" }); + assertThat(descriptor.toString(), is("Role[name=test, cluster=[all,none], indicesPrivileges=[IndicesPrivileges[indices=[i1,i2], " + + "privileges=[read], fields=[body,title], query={\"query\": {\"match_all\": {}}}],], runAs=[sudo]]")); } - public void testRDParsing() throws Exception { - String q; - RoleDescriptor rd; - try { - q = "{}"; - rd = RoleDescriptor.source(null, new BytesArray(q)); - fail("should have failed"); - } catch (ElasticsearchParseException e) { - // expected - } + public void testToXContent() throws Exception { + RoleDescriptor.IndicesPrivileges[] groups = new RoleDescriptor.IndicesPrivileges[] { + RoleDescriptor.IndicesPrivileges.builder() + .indices("i1", "i2") + .privileges("read") + .fields("body", "title") + .query("{\"query\": {\"match_all\": {}}}") + .build() + }; + RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, new String[] { "sudo" }); + XContentBuilder builder = descriptor.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS); + RoleDescriptor parsed = RoleDescriptor.parse("test", builder.bytes()); + assertThat(parsed, is(descriptor)); + } - q = "{\"name\": \"test\", \"cluster\":[\"a\", \"b\"]}"; - rd = RoleDescriptor.source(null, new BytesArray(q)); + public void testParse() throws Exception { + + String q = "{\"cluster\":[\"a\", \"b\"]}"; + RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q)); assertEquals("test", rd.getName()); - assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges()); + 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(null, new BytesArray(q)); + q = "{\"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"]}"; + rd = RoleDescriptor.parse("test", new BytesArray(q)); assertEquals("test", rd.getName()); - assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges()); + assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(0, rd.getIndicesPrivileges().length); - assertArrayEquals(new String[]{"m", "n"}, rd.getRunAs()); + 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(null, new BytesArray(q)); + q = "{\"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.parse("test", new BytesArray(q)); assertEquals("test", rd.getName()); - assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges()); + assertArrayEquals(new String[] { "a", "b" }, rd.getClusterPrivileges()); assertEquals(3, rd.getIndicesPrivileges().length); - assertArrayEquals(new String[]{"m", "n"}, rd.getRunAs()); + 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(null, new BytesArray(q)); + q = "{\"cluster\":[\"a\", \"b\"], \"run_as\": [\"m\", \"n\"], \"indices\": [{\"names\": [\"idx1\",\"idx2\"], \"privileges\": " + + "[\"p1\", \"p2\"]}]}"; + rd = RoleDescriptor.parse("test", new BytesArray(q)); assertEquals("test", rd.getName()); - assertArrayEquals(new String[]{"a", "b"}, rd.getClusterPrivileges()); + 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(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 ','")); - } - - try { - // 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(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")); - } + assertArrayEquals(new String[] { "idx1", "idx2" }, rd.getIndicesPrivileges()[0].getIndices()); + assertArrayEquals(new String[] { "m", "n" }, rd.getRunAs()); } } diff --git a/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/handlers b/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/handlers index f3670357bad..3282cdb2d8e 100644 --- a/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/handlers +++ b/elasticsearch/x-pack/shield/src/test/resources/org/elasticsearch/transport/handlers @@ -12,12 +12,12 @@ cluster:admin/shield/realm/cache/clear cluster:admin/shield/realm/cache/clear[n] cluster:admin/shield/roles/cache/clear cluster:admin/shield/roles/cache/clear[n] -cluster:admin/shield/user/put -cluster:admin/shield/user/delete -cluster:admin/shield/user/get cluster:admin/shield/role/put cluster:admin/shield/role/delete cluster:admin/shield/role/get +cluster:admin/shield/user/put +cluster:admin/shield/user/delete +cluster:admin/shield/user/get indices:admin/analyze[s] indices:admin/cache/clear[n] indices:admin/forcemerge[n] diff --git a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/api/shield.put_role.json b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/api/shield.put_role.json index 66a30fa6853..a49c4f0b9d5 100644 --- a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/api/shield.put_role.json +++ b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/api/shield.put_role.json @@ -8,7 +8,7 @@ "parts": { "name": { "type" : "string", - "description" : "Role Name", + "description" : "Role name", "required" : true } }, diff --git a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/10_basic.yaml b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/10_basic.yaml index c99e7cc2d09..c44d6ce99fa 100644 --- a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/10_basic.yaml +++ b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/10_basic.yaml @@ -12,7 +12,6 @@ name: "admin_role" body: > { - "name": "admin_role", "cluster": ["all"], "indices": [ { @@ -44,8 +43,6 @@ - do: shield.get_role: name: "admin_role" - - match: { found: true } - - match: { roles.0.name: "admin_role" } - - match: { roles.0.cluster.0: "all" } - - match: { roles.0.indices.0.names.0: "*" } - - match: { roles.0.indices.0.privileges.0: "all" } + - match: { admin_role.cluster.0: "all" } + - match: { admin_role.indices.0.names.0: "*" } + - match: { admin_role.indices.0.privileges.0: "all" } diff --git a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/11_idx_arrays.yaml b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/11_idx_arrays.yaml index 035d51f365e..75b48468e65 100644 --- a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/11_idx_arrays.yaml +++ b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/11_idx_arrays.yaml @@ -12,7 +12,6 @@ name: "admin_role2" body: > { - "name": "admin_role2", "cluster": ["all"], "indices": [ { @@ -65,9 +64,7 @@ - do: shield.get_role: name: "admin_role2" - - match: { found: true } - - match: { roles.0.name: "admin_role2" } - - match: { roles.0.cluster.0: "all" } - - match: { roles.0.indices.0.names.0: "foo" } - - match: { roles.0.indices.0.names.1: "bar" } - - match: { roles.0.indices.0.privileges.0: "all" } + - match: { admin_role2.cluster.0: "all" } + - match: { admin_role2.indices.0.names.0: "foo" } + - match: { admin_role2.indices.0.names.1: "bar" } + - match: { admin_role2.indices.0.privileges.0: "all" } diff --git a/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/20_get_missing.yaml b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/20_get_missing.yaml new file mode 100644 index 00000000000..cbfa09b8d33 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/test/resources/rest-api-spec/test/roles/20_get_missing.yaml @@ -0,0 +1,12 @@ +"Get missing role": + - do: + catch: missing + shield.get_role: + name: 'foo' + +--- +"Get missing (multiple) roles": + - do: + catch: missing + shield.get_role: + name: [ 'foo', 'bar' ] diff --git a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/xcontent/XContentUtils.java b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/xcontent/XContentUtils.java index 2b71c802511..0ad83294e18 100644 --- a/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/xcontent/XContentUtils.java +++ b/elasticsearch/x-pack/src/main/java/org/elasticsearch/xpack/common/xcontent/XContentUtils.java @@ -35,7 +35,7 @@ public class XContentUtils { } XContentParser.Token token = parser.nextToken(); if (token != XContentParser.Token.START_OBJECT) { - throw new ElasticsearchParseException("expected a user object, but found token [{}]", parser.currentToken()); + throw new ElasticsearchParseException("expected an object, but found token [{}]", parser.currentToken()); } }