Cleanup Security Roles
- Renamed `AddRoleAction/Request/Response` to `PutRoleAction/Request/Response` - also renamed the user/roles rest actions - Changed the returned format for `RestGetRoleAction`. Previously this endpoint returned an array of role descriptor. Now it returns an object where the role names serve as the keys for the role objects. This is aligned with other APIs in ES (e.g. index templates). - When `RestGetRoleAction` cannot find all the requested roles, it'll return an empty object and a 404 response status - Also cleaned up `RoleDescriptor` Original commit: elastic/x-pack-elasticsearch@742f6e0020
This commit is contained in:
parent
3ddbd77090
commit
2a1b3250db
|
@ -19,41 +19,37 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
|
|||
*/
|
||||
public class DeleteRoleRequest extends ActionRequest<DeleteRoleRequest> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ public class DeleteRoleRequestBuilder extends ActionRequestBuilder<DeleteRoleReq
|
|||
super(client, action, new DeleteRoleRequest());
|
||||
}
|
||||
|
||||
public DeleteRoleRequestBuilder name(String roleName) {
|
||||
request.role(roleName);
|
||||
public DeleteRoleRequestBuilder name(String name) {
|
||||
request.name(name);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,38 +20,37 @@ import static org.elasticsearch.action.ValidateActions.addValidationError;
|
|||
*/
|
||||
public class GetRolesRequest extends ActionRequest<GetRolesRequest> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ public class GetRolesRequestBuilder extends ActionRequestBuilder<GetRolesRequest
|
|||
super(client, action, new GetRolesRequest());
|
||||
}
|
||||
|
||||
public GetRolesRequestBuilder names(String... roles) {
|
||||
request.roles(roles);
|
||||
public GetRolesRequestBuilder names(String... names) {
|
||||
request.names(names);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,51 +11,40 @@ import org.elasticsearch.common.io.stream.StreamOutput;
|
|||
import org.elasticsearch.shield.authz.RoleDescriptor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Response when retrieving a role from the shield index. Does not contain a
|
||||
* real {@code Role} object, only a {@code RoleDescriptor}.
|
||||
* A response for the {@code Get Roles} API that holds the retrieved role descriptors.
|
||||
*/
|
||||
public class GetRolesResponse extends ActionResponse {
|
||||
private List<RoleDescriptor> roles;
|
||||
|
||||
public GetRolesResponse() {
|
||||
roles = Collections.emptyList();
|
||||
}
|
||||
private RoleDescriptor[] roles;
|
||||
|
||||
public GetRolesResponse(RoleDescriptor role) {
|
||||
this.roles = Collections.singletonList(role);
|
||||
}
|
||||
|
||||
public GetRolesResponse(List<RoleDescriptor> roles) {
|
||||
public GetRolesResponse(RoleDescriptor... roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public List<RoleDescriptor> 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);
|
||||
}
|
||||
|
|
|
@ -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<PutRoleRequest> implements ToXContent {
|
||||
public class PutRoleRequest extends ActionRequest<PutRoleRequest> {
|
||||
|
||||
private String name;
|
||||
private String[] clusterPrivileges;
|
||||
private String[] clusterPrivileges = Strings.EMPTY_ARRAY;
|
||||
private List<RoleDescriptor.IndicesPrivileges> indicesPrivileges = new ArrayList<>();
|
||||
private String[] runAs;
|
||||
|
||||
private String[] runAs = Strings.EMPTY_ARRAY;
|
||||
|
||||
public PutRoleRequest() {
|
||||
}
|
||||
|
||||
|
@ -44,14 +43,6 @@ 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;
|
||||
}
|
||||
|
@ -60,6 +51,10 @@ public class PutRoleRequest extends ActionRequest<PutRoleRequest> 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<PutRoleRequest> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<PutRoleRequest,
|
|||
}
|
||||
|
||||
public PutRoleRequestBuilder source(String name, BytesReference source) throws Exception {
|
||||
request.source(name, source);
|
||||
RoleDescriptor descriptor = RoleDescriptor.parse(name, source);
|
||||
request.name(descriptor.getName());
|
||||
request.cluster(descriptor.getClusterPrivileges());
|
||||
request.addIndex(descriptor.getIndicesPrivileges());
|
||||
request.runAs(descriptor.getRunAs());
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -33,8 +38,8 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
|
|||
return this;
|
||||
}
|
||||
|
||||
public PutRoleRequestBuilder cluster(String... clusterPrivileges) {
|
||||
request.cluster(clusterPrivileges);
|
||||
public PutRoleRequestBuilder cluster(String... cluster) {
|
||||
request.cluster(cluster);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -43,8 +48,8 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
|
|||
return this;
|
||||
}
|
||||
|
||||
public PutRoleRequestBuilder addIndices(String[] indices, String[] privileges, @Nullable String[] fields,
|
||||
@Nullable BytesReference query) {
|
||||
public PutRoleRequestBuilder addIndices(String[] indices, String[] privileges,
|
||||
@Nullable String[] fields, @Nullable BytesReference query) {
|
||||
request.addIndex(indices, privileges, fields, query);
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class TransportDeleteRoleAction extends HandledTransportAction<DeleteRole
|
|||
@Override
|
||||
protected void doExecute(DeleteRoleRequest request, ActionListener<DeleteRoleResponse> listener) {
|
||||
try {
|
||||
rolesStore.removeRole(request, new ActionListener<Boolean>() {
|
||||
rolesStore.deleteRole(request, new ActionListener<Boolean>() {
|
||||
@Override
|
||||
public void onResponse(Boolean found) {
|
||||
listener.onResponse(new DeleteRoleResponse(found));
|
||||
|
|
|
@ -33,9 +33,9 @@ public class TransportGetRolesAction extends HandledTransportAction<GetRolesRequ
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(GetRolesRequest request, ActionListener<GetRolesResponse> listener) {
|
||||
if (request.roles().length == 1) {
|
||||
final String rolename = request.roles()[0];
|
||||
protected void doExecute(final GetRolesRequest request, final ActionListener<GetRolesResponse> 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<RoleDescriptor>() {
|
||||
@Override
|
||||
|
@ -54,16 +54,16 @@ public class TransportGetRolesAction extends HandledTransportAction<GetRolesRequ
|
|||
}
|
||||
});
|
||||
} else {
|
||||
rolesStore.getRoleDescriptors(request.roles(), new ActionListener<List<RoleDescriptor>>() {
|
||||
rolesStore.getRoleDescriptors(request.names(), new ActionListener<List<RoleDescriptor>>() {
|
||||
@Override
|
||||
public void onResponse(List<RoleDescriptor> 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(), ","));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ public class TransportPutRoleAction extends HandledTransportAction<PutRoleReques
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(PutRoleRequest request, ActionListener<PutRoleResponse> listener) {
|
||||
rolesStore.addRole(request, new ActionListener<Boolean>() {
|
||||
protected void doExecute(final PutRoleRequest request, final ActionListener<PutRoleResponse> listener) {
|
||||
rolesStore.putRole(request.roleDescriptor(), new ActionListener<Boolean>() {
|
||||
@Override
|
||||
public void onResponse(Boolean created) {
|
||||
if (created) {
|
||||
|
|
|
@ -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<String> privs = new ArrayList<>();
|
||||
List<String> 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<String> 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<RoleDescriptor.IndicesPrivileges> parseIndices(XContentParser parser) throws Exception {
|
||||
XContentParser.Token token;
|
||||
List<RoleDescriptor.IndicesPrivileges> 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> indicesPrivileges = new ArrayList<>();
|
||||
List<String> runAsUsers = 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)) {
|
||||
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> 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<RoleDescriptor.IndicesPrivileges> 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<InternalClient> 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<List<RoleDescriptor>> listener) {
|
||||
public void getRoleDescriptors(String[] names, final ActionListener<List<RoleDescriptor>> 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<RoleDescriptor> 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<GetResponse> 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<Boolean> listener) {
|
||||
public void deleteRole(final DeleteRoleRequest deleteRoleRequest, final ActionListener<Boolean> 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<DeleteResponse>() {
|
||||
@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<Boolean> 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<IndexResponse>() {
|
||||
@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<GetResponse> 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<Boolean> listener) {
|
||||
if (state() != State.STARTED) {
|
||||
logger.trace("attempted to add role before service was started");
|
||||
listener.onResponse(false);
|
||||
}
|
||||
private void executeGetRoleRequest(String role, ActionListener<GetResponse> 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<IndexResponse>() {
|
||||
@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;
|
||||
|
|
|
@ -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<GetRolesResponse>(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<GetRolesResponse>(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);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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<RoleDescriptor> 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));
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"parts": {
|
||||
"name": {
|
||||
"type" : "string",
|
||||
"description" : "Role Name",
|
||||
"description" : "Role name",
|
||||
"required" : true
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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' ]
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue