HLRest: model role and privileges (#35128)
The following "user privileges" objects have been created on the client side: * ApplicationResourcePrivileges * IndicesPrivileges * GlobalOperationPrivilege * GlobalPrivileges as well as the aggregating `Role` entity.
This commit is contained in:
parent
eb8f346204
commit
6050deb6f0
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
|
||||
/**
|
||||
* Represents privileges over resources that are scoped under an application.
|
||||
* The application, resources and privileges are completely managed by the
|
||||
* client and can be arbitrary string identifiers. Elasticsearch is not
|
||||
* concerned by any resources under an application scope.
|
||||
*/
|
||||
public final class ApplicationResourcePrivileges implements ToXContentObject {
|
||||
|
||||
private static final ParseField APPLICATION = new ParseField("application");
|
||||
private static final ParseField PRIVILEGES = new ParseField("privileges");
|
||||
private static final ParseField RESOURCES = new ParseField("resources");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final ConstructingObjectParser<ApplicationResourcePrivileges, Void> PARSER = new ConstructingObjectParser<>(
|
||||
"application_privileges", false, constructorObjects -> {
|
||||
// Don't ignore unknown fields. It is dangerous if the object we parse is also
|
||||
// part of a request that we build later on, and the fields that we now ignore will
|
||||
// end up being implicitly set to null in that request.
|
||||
int i = 0;
|
||||
final String application = (String) constructorObjects[i++];
|
||||
final Collection<String> privileges = (Collection<String>) constructorObjects[i++];
|
||||
final Collection<String> resources = (Collection<String>) constructorObjects[i];
|
||||
return new ApplicationResourcePrivileges(application, privileges, resources);
|
||||
});
|
||||
|
||||
static {
|
||||
PARSER.declareString(constructorArg(), APPLICATION);
|
||||
PARSER.declareStringArray(constructorArg(), PRIVILEGES);
|
||||
PARSER.declareStringArray(constructorArg(), RESOURCES);
|
||||
}
|
||||
|
||||
private final String application;
|
||||
private final Set<String> privileges;
|
||||
private final Set<String> resources;
|
||||
|
||||
/**
|
||||
* Constructs privileges for resources under an application scope.
|
||||
*
|
||||
* @param application
|
||||
* The application name. This identifier is completely under the
|
||||
* clients control.
|
||||
* @param privileges
|
||||
* The privileges names. Cannot be null or empty. Privilege
|
||||
* identifiers are completely under the clients control.
|
||||
* @param resources
|
||||
* The resources names. Cannot be null or empty. Resource identifiers
|
||||
* are completely under the clients control.
|
||||
*/
|
||||
public ApplicationResourcePrivileges(String application, Collection<String> privileges, Collection<String> resources) {
|
||||
if (Strings.isNullOrEmpty(application)) {
|
||||
throw new IllegalArgumentException("application privileges must have an application name");
|
||||
}
|
||||
if (null == privileges || privileges.isEmpty()) {
|
||||
throw new IllegalArgumentException("application privileges must define at least one privilege");
|
||||
}
|
||||
if (null == resources || resources.isEmpty()) {
|
||||
throw new IllegalArgumentException("application privileges must refer to at least one resource");
|
||||
}
|
||||
this.application = application;
|
||||
this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges));
|
||||
this.resources = Collections.unmodifiableSet(new HashSet<>(resources));
|
||||
}
|
||||
|
||||
public String getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public Set<String> getResources() {
|
||||
return this.resources;
|
||||
}
|
||||
|
||||
public Set<String> getPrivileges() {
|
||||
return this.privileges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ApplicationResourcePrivileges that = (ApplicationResourcePrivileges) o;
|
||||
return application.equals(that.application)
|
||||
&& privileges.equals(that.privileges)
|
||||
&& resources.equals(that.resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(application, privileges, resources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unexpected", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(APPLICATION.getPreferredName(), application);
|
||||
builder.field(PRIVILEGES.getPreferredName(), privileges);
|
||||
builder.field(RESOURCES.getPreferredName(), resources);
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static ApplicationResourcePrivileges fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents generic global cluster privileges that can be scoped by categories
|
||||
* and then further by operations. The privilege's syntactic and semantic
|
||||
* meaning is specific to each category and operation; there is no general
|
||||
* definition template. It is not permitted to define different privileges under
|
||||
* the same category and operation.
|
||||
*/
|
||||
public class GlobalOperationPrivilege {
|
||||
|
||||
private final String category;
|
||||
private final String operation;
|
||||
private final Map<String, Object> privilege;
|
||||
|
||||
/**
|
||||
* Constructs privileges under a specific {@code category} and for some
|
||||
* {@code operation}. The privilege definition is flexible, it is a {@code Map},
|
||||
* and the semantics is bound to the {@code category} and {@code operation}.
|
||||
*
|
||||
* @param category
|
||||
* The category of the privilege.
|
||||
* @param operation
|
||||
* The operation of the privilege.
|
||||
* @param privilege
|
||||
* The privilege definition.
|
||||
*/
|
||||
public GlobalOperationPrivilege(String category, String operation, Map<String, Object> privilege) {
|
||||
this.category = Objects.requireNonNull(category);
|
||||
this.operation = Objects.requireNonNull(operation);
|
||||
if (privilege == null || privilege.isEmpty()) {
|
||||
throw new IllegalArgumentException("Privileges cannot be empty or null");
|
||||
}
|
||||
this.privilege = Collections.unmodifiableMap(privilege);
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public Map<String, Object> getRaw() {
|
||||
return privilege;
|
||||
}
|
||||
|
||||
public static GlobalOperationPrivilege fromXContent(String category, String operation, XContentParser parser) throws IOException {
|
||||
// parser is still placed on the field name, advance to next token (field value)
|
||||
assert parser.currentToken().equals(XContentParser.Token.FIELD_NAME);
|
||||
parser.nextToken();
|
||||
return new GlobalOperationPrivilege(category, operation, parser.map());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || (false == this instanceof GlobalOperationPrivilege)) {
|
||||
return false;
|
||||
}
|
||||
final GlobalOperationPrivilege that = (GlobalOperationPrivilege) o;
|
||||
return category.equals(that.category) && operation.equals(that.operation) && privilege.equals(that.privilege);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(category, operation, privilege);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
|
||||
/**
|
||||
* Represents global privileges. "Global Privilege" is a mantra for granular
|
||||
* generic cluster privileges. These privileges are organized into categories.
|
||||
* Elasticsearch defines the set of categories. Under each category there are
|
||||
* operations that are under the clients jurisdiction. The privilege is hence
|
||||
* defined under an operation under a category.
|
||||
*/
|
||||
public final class GlobalPrivileges implements ToXContentObject {
|
||||
|
||||
// When categories change, adapting this field should suffice. Categories are NOT
|
||||
// opaque "named_objects", we wish to maintain control over these namespaces
|
||||
static final List<String> CATEGORIES = Collections.unmodifiableList(Arrays.asList("application"));
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final ConstructingObjectParser<GlobalPrivileges, Void> PARSER = new ConstructingObjectParser<>("global_category_privileges",
|
||||
false, constructorObjects -> {
|
||||
// ignore_unknown_fields is irrelevant here anyway, but let's keep it to false
|
||||
// because this conveys strictness (woop woop)
|
||||
return new GlobalPrivileges((Collection<GlobalOperationPrivilege>) constructorObjects[0]);
|
||||
});
|
||||
|
||||
static {
|
||||
for (final String category : CATEGORIES) {
|
||||
PARSER.declareNamedObjects(optionalConstructorArg(),
|
||||
(parser, context, operation) -> GlobalOperationPrivilege.fromXContent(category, operation, parser),
|
||||
new ParseField(category));
|
||||
}
|
||||
}
|
||||
|
||||
private final Set<? extends GlobalOperationPrivilege> privileges;
|
||||
// same data as in privileges but broken down by categories; internally, it is
|
||||
// easier to work with this structure
|
||||
private final Map<String, List<GlobalOperationPrivilege>> privilegesByCategoryMap;
|
||||
|
||||
/**
|
||||
* Constructs global privileges by bundling the set of privileges.
|
||||
*
|
||||
* @param privileges
|
||||
* The privileges under a category and for an operation in that category.
|
||||
*/
|
||||
public GlobalPrivileges(Collection<? extends GlobalOperationPrivilege> privileges) {
|
||||
if (privileges == null || privileges.isEmpty()) {
|
||||
throw new IllegalArgumentException("Privileges cannot be empty or null");
|
||||
}
|
||||
// duplicates are just ignored
|
||||
this.privileges = Collections.unmodifiableSet(new HashSet<>(Objects.requireNonNull(privileges)));
|
||||
this.privilegesByCategoryMap = Collections
|
||||
.unmodifiableMap(this.privileges.stream().collect(Collectors.groupingBy(GlobalOperationPrivilege::getCategory)));
|
||||
for (final Map.Entry<String, List<GlobalOperationPrivilege>> privilegesByCategory : privilegesByCategoryMap.entrySet()) {
|
||||
// all operations for a specific category
|
||||
final Set<String> allOperations = privilegesByCategory.getValue().stream().map(p -> p.getOperation())
|
||||
.collect(Collectors.toSet());
|
||||
if (allOperations.size() != privilegesByCategory.getValue().size()) {
|
||||
throw new IllegalArgumentException("Different privileges for the same category and operation are not permitted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
for (final Map.Entry<String, List<GlobalOperationPrivilege>> privilegesByCategory : this.privilegesByCategoryMap.entrySet()) {
|
||||
builder.startObject(privilegesByCategory.getKey());
|
||||
for (final GlobalOperationPrivilege privilege : privilegesByCategory.getValue()) {
|
||||
builder.field(privilege.getOperation(), privilege.getRaw());
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static GlobalPrivileges fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public Set<? extends GlobalOperationPrivilege> getPrivileges() {
|
||||
return privileges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final GlobalPrivileges that = (GlobalPrivileges) o;
|
||||
return privileges.equals(that.privileges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(privileges);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
|
||||
/**
|
||||
* Represents privileges over indices. There is a canonical set of privilege
|
||||
* names (eg. {@code IndicesPrivileges#READ_PRIVILEGE_NAME}) but there is
|
||||
* flexibility in the definition of finer grained, more specialized, privileges.
|
||||
* This also encapsulates field and document level security privileges. These
|
||||
* allow to control what fields or documents are readable or queryable.
|
||||
*/
|
||||
public final class IndicesPrivileges implements ToXContentObject {
|
||||
|
||||
public static final ParseField NAMES = new ParseField("names");
|
||||
public static final ParseField PRIVILEGES = new ParseField("privileges");
|
||||
public static final ParseField FIELD_PERMISSIONS = new ParseField("field_security");
|
||||
public static final ParseField GRANT_FIELDS = new ParseField("grant");
|
||||
public static final ParseField EXCEPT_FIELDS = new ParseField("except");
|
||||
public static final ParseField QUERY = new ParseField("query");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static final ConstructingObjectParser<IndicesPrivileges, Void> PARSER =
|
||||
new ConstructingObjectParser<>("indices_privileges", false, constructorObjects -> {
|
||||
int i = 0;
|
||||
final Collection<String> indices = (Collection<String>) constructorObjects[i++];
|
||||
final Collection<String> privileges = (Collection<String>) constructorObjects[i++];
|
||||
final Tuple<Collection<String>, Collection<String>> fields =
|
||||
(Tuple<Collection<String>, Collection<String>>) constructorObjects[i++];
|
||||
final Collection<String> grantFields = fields != null ? fields.v1() : null;
|
||||
final Collection<String> exceptFields = fields != null ? fields.v2() : null;
|
||||
final String query = (String) constructorObjects[i];
|
||||
return new IndicesPrivileges(indices, privileges, grantFields, exceptFields, query);
|
||||
});
|
||||
|
||||
static {
|
||||
@SuppressWarnings("unchecked")
|
||||
final ConstructingObjectParser<Tuple<Collection<String>, Collection<String>>, Void> fls_parser =
|
||||
new ConstructingObjectParser<>( "field_level_parser", false, constructorObjects -> {
|
||||
int i = 0;
|
||||
final Collection<String> grantFields = (Collection<String>) constructorObjects[i++];
|
||||
final Collection<String> exceptFields = (Collection<String>) constructorObjects[i];
|
||||
return new Tuple<>(grantFields, exceptFields);
|
||||
});
|
||||
fls_parser.declareStringArray(optionalConstructorArg(), GRANT_FIELDS);
|
||||
fls_parser.declareStringArray(optionalConstructorArg(), EXCEPT_FIELDS);
|
||||
|
||||
PARSER.declareStringArray(constructorArg(), NAMES);
|
||||
PARSER.declareStringArray(constructorArg(), PRIVILEGES);
|
||||
PARSER.declareObject(optionalConstructorArg(), fls_parser, FIELD_PERMISSIONS);
|
||||
PARSER.declareStringOrNull(optionalConstructorArg(), QUERY);
|
||||
}
|
||||
|
||||
private final Set<String> indices;
|
||||
private final Set<String> privileges;
|
||||
// null or singleton '*' means all fields are granted, empty means no fields are granted
|
||||
private final @Nullable Set<String> grantedFields;
|
||||
// null or empty means no fields are denied
|
||||
private final @Nullable Set<String> deniedFields;
|
||||
// missing query means all documents, i.e. no restrictions
|
||||
private final @Nullable String query;
|
||||
|
||||
private IndicesPrivileges(Collection<String> indices, Collection<String> privileges, @Nullable Collection<String> grantedFields,
|
||||
@Nullable Collection<String> deniedFields, @Nullable String query) {
|
||||
if (null == indices || indices.isEmpty()) {
|
||||
throw new IllegalArgumentException("indices privileges must refer to at least one index name or index name pattern");
|
||||
}
|
||||
if (null == privileges || privileges.isEmpty()) {
|
||||
throw new IllegalArgumentException("indices privileges must define at least one privilege");
|
||||
}
|
||||
this.indices = Collections.unmodifiableSet(new HashSet<>(indices));
|
||||
this.privileges = Collections.unmodifiableSet(new HashSet<>(privileges));
|
||||
// unspecified granted fields means no restriction
|
||||
this.grantedFields = grantedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(grantedFields));
|
||||
// unspecified denied fields means no restriction
|
||||
this.deniedFields = deniedFields == null ? null : Collections.unmodifiableSet(new HashSet<>(deniedFields));
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
/**
|
||||
* The indices names covered by the privileges.
|
||||
*/
|
||||
public Set<String> getIndices() {
|
||||
return this.indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* The privileges acting over indices. There is a canonical predefined set of
|
||||
* such privileges, but the {@code String} datatype allows for flexibility in defining
|
||||
* finer grained privileges.
|
||||
*/
|
||||
public Set<String> getPrivileges() {
|
||||
return this.privileges;
|
||||
}
|
||||
|
||||
/**
|
||||
* The document fields that can be read or queried. Can be null, in this case
|
||||
* all the document's fields are granted access to. Can also be empty, in which
|
||||
* case no fields are granted access to.
|
||||
*/
|
||||
public @Nullable Set<String> getGrantedFields() {
|
||||
return this.grantedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* The document fields that cannot be accessed or queried. Can be null or empty,
|
||||
* in which case no fields are denied.
|
||||
*/
|
||||
public @Nullable Set<String> getDeniedFields() {
|
||||
return this.deniedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* A query limiting the visible documents in the indices. Can be null, in which
|
||||
* case all documents are visible.
|
||||
*/
|
||||
public @Nullable String getQuery() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} some documents might not be visible. Only the documents
|
||||
* matching {@code query} will be readable.
|
||||
*/
|
||||
public boolean isUsingDocumentLevelSecurity() {
|
||||
return query != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code true} some document fields might not be visible.
|
||||
*/
|
||||
public boolean isUsingFieldLevelSecurity() {
|
||||
return limitsGrantedFields() || hasDeniedFields();
|
||||
}
|
||||
|
||||
private boolean hasDeniedFields() {
|
||||
return deniedFields != null && false == deniedFields.isEmpty();
|
||||
}
|
||||
|
||||
private boolean limitsGrantedFields() {
|
||||
// we treat just '*' as no FLS since that's what the UI defaults to
|
||||
if (grantedFields == null || (grantedFields.size() == 1 && grantedFields.iterator().next().equals("*"))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
IndicesPrivileges that = (IndicesPrivileges) o;
|
||||
return indices.equals(that.indices)
|
||||
&& privileges.equals(that.privileges)
|
||||
&& Objects.equals(grantedFields, that.grantedFields)
|
||||
&& Objects.equals(deniedFields, that.deniedFields)
|
||||
&& Objects.equals(query, that.query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(indices, privileges, grantedFields, deniedFields, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unexpected", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field(NAMES.getPreferredName(), indices);
|
||||
builder.field(PRIVILEGES.getPreferredName(), privileges);
|
||||
if (isUsingFieldLevelSecurity()) {
|
||||
builder.startObject(FIELD_PERMISSIONS.getPreferredName());
|
||||
if (grantedFields != null) {
|
||||
builder.field(GRANT_FIELDS.getPreferredName(), grantedFields);
|
||||
}
|
||||
if (hasDeniedFields()) {
|
||||
builder.field(EXCEPT_FIELDS.getPreferredName(), deniedFields);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
if (isUsingDocumentLevelSecurity()) {
|
||||
builder.field("query", query);
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static IndicesPrivileges fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
private @Nullable Collection<String> indices = null;
|
||||
private @Nullable Collection<String> privileges = null;
|
||||
private @Nullable Collection<String> grantedFields = null;
|
||||
private @Nullable Collection<String> deniedFields = null;
|
||||
private @Nullable String query = null;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder indices(String... indices) {
|
||||
return indices(Arrays.asList(Objects.requireNonNull(indices, "indices required")));
|
||||
}
|
||||
|
||||
public Builder indices(Collection<String> indices) {
|
||||
this.indices = Objects.requireNonNull(indices, "indices required");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder privileges(String... privileges) {
|
||||
return privileges(Arrays.asList(Objects.requireNonNull(privileges, "privileges required")));
|
||||
}
|
||||
|
||||
public Builder privileges(Collection<String> privileges) {
|
||||
this.privileges = Objects.requireNonNull(privileges, "privileges required");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder grantedFields(@Nullable String... grantedFields) {
|
||||
if (grantedFields == null) {
|
||||
this.grantedFields = null;
|
||||
return this;
|
||||
}
|
||||
return grantedFields(Arrays.asList(grantedFields));
|
||||
}
|
||||
|
||||
public Builder grantedFields(@Nullable Collection<String> grantedFields) {
|
||||
this.grantedFields = grantedFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder deniedFields(@Nullable String... deniedFields) {
|
||||
if (deniedFields == null) {
|
||||
this.deniedFields = null;
|
||||
return this;
|
||||
}
|
||||
return deniedFields(Arrays.asList(deniedFields));
|
||||
}
|
||||
|
||||
public Builder deniedFields(@Nullable Collection<String> deniedFields) {
|
||||
this.deniedFields = deniedFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder query(@Nullable String query) {
|
||||
this.query = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IndicesPrivileges build() {
|
||||
return new IndicesPrivileges(indices, privileges, grantedFields, deniedFields, query);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents the privilege to "manage" certain applications. The "manage"
|
||||
* privilege is actually defined outside of Elasticsearch.
|
||||
*/
|
||||
public class ManageApplicationPrivilege extends GlobalOperationPrivilege {
|
||||
|
||||
private static final String CATEGORY = "application";
|
||||
private static final String OPERATION = "manage";
|
||||
private static final String KEY = "applications";
|
||||
|
||||
public ManageApplicationPrivilege(Collection<String> applications) {
|
||||
super(CATEGORY, OPERATION, Collections.singletonMap(KEY, new HashSet<String>(Objects.requireNonNull(applications))));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Set<String> getManagedApplications() {
|
||||
return (Set<String>)getRaw().get(KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentType;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
|
||||
/**
|
||||
* Represents an aggregation of privileges. This does not have a name
|
||||
* identifier.
|
||||
*/
|
||||
public final class Role implements ToXContentObject {
|
||||
|
||||
public static final ParseField CLUSTER = new ParseField("cluster");
|
||||
public static final ParseField GLOBAL = new ParseField("global");
|
||||
public static final ParseField INDICES = new ParseField("indices");
|
||||
public static final ParseField APPLICATIONS = new ParseField("applications");
|
||||
public static final ParseField RUN_AS = new ParseField("run_as");
|
||||
public static final ParseField METADATA = new ParseField("metadata");
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final ConstructingObjectParser<Role, Void> PARSER = new ConstructingObjectParser<>("role_descriptor", false,
|
||||
constructorObjects -> {
|
||||
// Don't ignore unknown fields. It is dangerous if the object we parse is also
|
||||
// part of a request that we build later on, and the fields that we now ignore
|
||||
// will end up being implicitly set to null in that request.
|
||||
int i = 0;
|
||||
final Collection<String> clusterPrivileges = (Collection<String>) constructorObjects[i++];
|
||||
final GlobalPrivileges globalApplicationPrivileges = (GlobalPrivileges) constructorObjects[i++];
|
||||
final Collection<IndicesPrivileges> indicesPrivileges = (Collection<IndicesPrivileges>) constructorObjects[i++];
|
||||
final Collection<ApplicationResourcePrivileges> applicationResourcePrivileges =
|
||||
(Collection<ApplicationResourcePrivileges>) constructorObjects[i++];
|
||||
final Collection<String> runAsPrivilege = (Collection<String>) constructorObjects[i++];
|
||||
final Map<String, Object> metadata = (Map<String, Object>) constructorObjects[i];
|
||||
return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
});
|
||||
|
||||
static {
|
||||
PARSER.declareStringArray(optionalConstructorArg(), CLUSTER);
|
||||
PARSER.declareObject(optionalConstructorArg(), GlobalPrivileges.PARSER, GLOBAL);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), IndicesPrivileges.PARSER, INDICES, ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareFieldArray(optionalConstructorArg(), ApplicationResourcePrivileges.PARSER, APPLICATIONS, ValueType.OBJECT_ARRAY);
|
||||
PARSER.declareStringArray(optionalConstructorArg(), RUN_AS);
|
||||
PARSER.declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA);
|
||||
}
|
||||
|
||||
private final Set<String> clusterPrivileges;
|
||||
private final @Nullable GlobalPrivileges globalApplicationPrivileges;
|
||||
private final Set<IndicesPrivileges> indicesPrivileges;
|
||||
private final Set<ApplicationResourcePrivileges> applicationResourcePrivileges;
|
||||
private final Set<String> runAsPrivilege;
|
||||
private final Map<String, Object> metadata;
|
||||
|
||||
private Role(@Nullable Collection<String> clusterPrivileges, @Nullable GlobalPrivileges globalApplicationPrivileges,
|
||||
@Nullable Collection<IndicesPrivileges> indicesPrivileges,
|
||||
@Nullable Collection<ApplicationResourcePrivileges> applicationResourcePrivileges, @Nullable Collection<String> runAsPrivilege,
|
||||
@Nullable Map<String, Object> metadata) {
|
||||
// no cluster privileges are granted unless otherwise specified
|
||||
this.clusterPrivileges = Collections
|
||||
.unmodifiableSet(clusterPrivileges != null ? new HashSet<>(clusterPrivileges) : Collections.emptySet());
|
||||
this.globalApplicationPrivileges = globalApplicationPrivileges;
|
||||
// no indices privileges are granted unless otherwise specified
|
||||
this.indicesPrivileges = Collections
|
||||
.unmodifiableSet(indicesPrivileges != null ? new HashSet<>(indicesPrivileges) : Collections.emptySet());
|
||||
// no application resource privileges are granted unless otherwise specified
|
||||
this.applicationResourcePrivileges = Collections.unmodifiableSet(
|
||||
applicationResourcePrivileges != null ? new HashSet<>(applicationResourcePrivileges) : Collections.emptySet());
|
||||
// no run as privileges are granted unless otherwise specified
|
||||
this.runAsPrivilege = Collections.unmodifiableSet(runAsPrivilege != null ? new HashSet<>(runAsPrivilege) : Collections.emptySet());
|
||||
this.metadata = metadata != null ? Collections.unmodifiableMap(metadata) : Collections.emptyMap();
|
||||
}
|
||||
|
||||
public Set<String> getClusterPrivileges() {
|
||||
return clusterPrivileges;
|
||||
}
|
||||
|
||||
public GlobalPrivileges getGlobalApplicationPrivileges() {
|
||||
return globalApplicationPrivileges;
|
||||
}
|
||||
|
||||
public Set<IndicesPrivileges> getIndicesPrivileges() {
|
||||
return indicesPrivileges;
|
||||
}
|
||||
|
||||
public Set<ApplicationResourcePrivileges> getApplicationResourcePrivileges() {
|
||||
return applicationResourcePrivileges;
|
||||
}
|
||||
|
||||
public Set<String> getRunAsPrivilege() {
|
||||
return runAsPrivilege;
|
||||
}
|
||||
|
||||
public Map<String, Object> getMetadata() {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Role that = (Role) o;
|
||||
return clusterPrivileges.equals(that.clusterPrivileges)
|
||||
&& Objects.equals(globalApplicationPrivileges, that.globalApplicationPrivileges)
|
||||
&& indicesPrivileges.equals(that.indicesPrivileges)
|
||||
&& applicationResourcePrivileges.equals(that.applicationResourcePrivileges)
|
||||
&& runAsPrivilege.equals(that.runAsPrivilege)
|
||||
&& metadata.equals(that.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return XContentHelper.toXContent(this, XContentType.JSON, true).utf8ToString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unexpected", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
if (false == clusterPrivileges.isEmpty()) {
|
||||
builder.field(CLUSTER.getPreferredName(), clusterPrivileges);
|
||||
}
|
||||
if (null != globalApplicationPrivileges) {
|
||||
builder.field(GLOBAL.getPreferredName(), globalApplicationPrivileges);
|
||||
}
|
||||
if (false == indicesPrivileges.isEmpty()) {
|
||||
builder.field(INDICES.getPreferredName(), indicesPrivileges);
|
||||
}
|
||||
if (false == applicationResourcePrivileges.isEmpty()) {
|
||||
builder.field(APPLICATIONS.getPreferredName(), applicationResourcePrivileges);
|
||||
}
|
||||
if (false == runAsPrivilege.isEmpty()) {
|
||||
builder.field(RUN_AS.getPreferredName(), runAsPrivilege);
|
||||
}
|
||||
if (false == metadata.isEmpty()) {
|
||||
builder.field(METADATA.getPreferredName(), metadata);
|
||||
}
|
||||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static Role fromXContent(XContentParser parser) {
|
||||
return PARSER.apply(parser, null);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
private @Nullable Collection<String> clusterPrivileges = null;
|
||||
private @Nullable GlobalPrivileges globalApplicationPrivileges = null;
|
||||
private @Nullable Collection<IndicesPrivileges> indicesPrivileges = null;
|
||||
private @Nullable Collection<ApplicationResourcePrivileges> applicationResourcePrivileges = null;
|
||||
private @Nullable Collection<String> runAsPrivilege = null;
|
||||
private @Nullable Map<String, Object> metadata = null;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder clusterPrivileges(String... clusterPrivileges) {
|
||||
return clusterPrivileges(Arrays
|
||||
.asList(Objects.requireNonNull(clusterPrivileges, "Cluster privileges cannot be null. Pass an empty array instead.")));
|
||||
}
|
||||
|
||||
public Builder clusterPrivileges(Collection<String> clusterPrivileges) {
|
||||
this.clusterPrivileges = Objects.requireNonNull(clusterPrivileges,
|
||||
"Cluster privileges cannot be null. Pass an empty collection instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder glabalApplicationPrivileges(GlobalPrivileges globalApplicationPrivileges) {
|
||||
this.globalApplicationPrivileges = globalApplicationPrivileges;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder indicesPrivileges(IndicesPrivileges... indicesPrivileges) {
|
||||
return indicesPrivileges(Arrays
|
||||
.asList(Objects.requireNonNull(indicesPrivileges, "Indices privileges cannot be null. Pass an empty array instead.")));
|
||||
}
|
||||
|
||||
public Builder indicesPrivileges(Collection<IndicesPrivileges> indicesPrivileges) {
|
||||
this.indicesPrivileges = Objects.requireNonNull(indicesPrivileges,
|
||||
"Indices privileges cannot be null. Pass an empty collection instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder applicationResourcePrivileges(ApplicationResourcePrivileges... applicationResourcePrivileges) {
|
||||
return applicationResourcePrivileges(Arrays.asList(Objects.requireNonNull(applicationResourcePrivileges,
|
||||
"Application resource privileges cannot be null. Pass an empty array instead.")));
|
||||
}
|
||||
|
||||
public Builder applicationResourcePrivileges(Collection<ApplicationResourcePrivileges> applicationResourcePrivileges) {
|
||||
this.applicationResourcePrivileges = Objects.requireNonNull(applicationResourcePrivileges,
|
||||
"Application resource privileges cannot be null. Pass an empty collection instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder runAsPrivilege(String... runAsPrivilege) {
|
||||
return runAsPrivilege(Arrays
|
||||
.asList(Objects.requireNonNull(runAsPrivilege, "Run as privilege cannot be null. Pass an empty array instead.")));
|
||||
}
|
||||
|
||||
public Builder runAsPrivilege(Collection<String> runAsPrivilege) {
|
||||
this.runAsPrivilege = Objects.requireNonNull(runAsPrivilege,
|
||||
"Run as privilege cannot be null. Pass an empty collection instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder metadata(Map<String, Object> metadata) {
|
||||
this.metadata = Objects.requireNonNull(metadata, "Metadata cannot be null. Pass an empty map instead.");
|
||||
return this;
|
||||
}
|
||||
|
||||
public Role build() {
|
||||
return new Role(clusterPrivileges, globalApplicationPrivileges, indicesPrivileges, applicationResourcePrivileges,
|
||||
runAsPrivilege, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical cluster privilege names. There is no enforcement to only use these.
|
||||
*/
|
||||
public static class ClusterPrivilegeName {
|
||||
public static final String NONE = "none";
|
||||
public static final String ALL = "all";
|
||||
public static final String MONITOR = "monitor";
|
||||
public static final String MONITOR_ML = "monitor_ml";
|
||||
public static final String MONITOR_WATCHER = "monitor_watcher";
|
||||
public static final String MONITOR_ROLLUP = "monitor_rollup";
|
||||
public static final String MANAGE = "manage";
|
||||
public static final String MANAGE_ML = "manage_ml";
|
||||
public static final String MANAGE_WATCHER = "manage_watcher";
|
||||
public static final String MANAGE_ROLLUP = "manage_rollup";
|
||||
public static final String MANAGE_INDEX_TEMPLATES = "manage_index_templates";
|
||||
public static final String MANAGE_INGEST_PIPELINES = "manage_ingest_pipelines";
|
||||
public static final String TRANSPORT_CLIENT = "transport_client";
|
||||
public static final String MANAGE_SECURITY = "manage_security";
|
||||
public static final String MANAGE_SAML = "manage_saml";
|
||||
public static final String MANAGE_PIPELINE = "manage_pipeline";
|
||||
public static final String MANAGE_CCR = "manage_ccr";
|
||||
public static final String READ_CCR = "read_ccr";
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonical index privilege names. There is no enforcement to only use these.
|
||||
*/
|
||||
public static class IndexPrivilegeName {
|
||||
public static final String NONE = "none";
|
||||
public static final String ALL = "all";
|
||||
public static final String READ = "read";
|
||||
public static final String READ_CROSS = "read_cross_cluster";
|
||||
public static final String CREATE = "create";
|
||||
public static final String INDEX = "index";
|
||||
public static final String DELETE = "delete";
|
||||
public static final String WRITE = "write";
|
||||
public static final String MONITOR = "monitor";
|
||||
public static final String MANAGE = "manage";
|
||||
public static final String DELETE_INDEX = "delete_index";
|
||||
public static final String CREATE_INDEX = "create_index";
|
||||
public static final String VIEW_INDEX_METADATA = "view_index_metadata";
|
||||
public static final String MANAGE_FOLLOW_INDEX = "manage_follow_index";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class ApplicationResourcePrivilegesTests extends AbstractXContentTestCase<ApplicationResourcePrivileges> {
|
||||
|
||||
@Override
|
||||
protected ApplicationResourcePrivileges createTestInstance() {
|
||||
return new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8),
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))),
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApplicationResourcePrivileges doParseInstance(XContentParser parser) throws IOException {
|
||||
return ApplicationResourcePrivileges.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void testEmptyApplicationName() {
|
||||
final String emptyApplicationName = randomBoolean() ? "" : null;
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new ApplicationResourcePrivileges(emptyApplicationName,
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))),
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8)))));
|
||||
assertThat(e.getMessage(), is("application privileges must have an application name"));
|
||||
}
|
||||
|
||||
public void testEmptyPrivileges() {
|
||||
final Collection<String> emptyPrivileges = randomBoolean() ? Collections.emptyList() : null;
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8),
|
||||
emptyPrivileges,
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8)))));
|
||||
assertThat(e.getMessage(), is("application privileges must define at least one privilege"));
|
||||
}
|
||||
|
||||
public void testEmptyResources() {
|
||||
final Collection<String> emptyResources = randomBoolean() ? Collections.emptyList() : null;
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new ApplicationResourcePrivileges(randomAlphaOfLengthBetween(1, 8),
|
||||
Arrays.asList(randomArray(1, 8, size -> new String[size], () -> randomAlphaOfLengthBetween(1, 8))),
|
||||
emptyResources));
|
||||
assertThat(e.getMessage(), is("application privileges must refer to at least one resource"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.client.security.user.privileges;
|
||||
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.test.AbstractXContentTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class GlobalPrivilegesTests extends AbstractXContentTestCase<GlobalPrivileges> {
|
||||
|
||||
private static long idCounter = 0;
|
||||
|
||||
@Override
|
||||
protected GlobalPrivileges createTestInstance() {
|
||||
final List<GlobalOperationPrivilege> privilegeList = Arrays
|
||||
.asList(randomArray(1, 4, size -> new GlobalOperationPrivilege[size], () -> buildRandomGlobalScopedPrivilege()));
|
||||
return new GlobalPrivileges(privilegeList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GlobalPrivileges doParseInstance(XContentParser parser) throws IOException {
|
||||
return GlobalPrivileges.fromXContent(parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsUnknownFields() {
|
||||
return false; // true really means inserting bogus privileges
|
||||
}
|
||||
|
||||
public void testEmptyOrNullGlobalOperationPrivilege() {
|
||||
final Map<String, Object> privilege = randomBoolean() ? null : Collections.emptyMap();
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new GlobalOperationPrivilege(randomAlphaOfLength(2), randomAlphaOfLength(2), privilege));
|
||||
assertThat(e.getMessage(), is("Privileges cannot be empty or null"));
|
||||
}
|
||||
|
||||
public void testEmptyOrNullGlobalPrivileges() {
|
||||
final List<GlobalOperationPrivilege> privileges = randomBoolean() ? null : Collections.emptyList();
|
||||
final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GlobalPrivileges(privileges));
|
||||
assertThat(e.getMessage(), is("Privileges cannot be empty or null"));
|
||||
}
|
||||
|
||||
public void testDuplicateGlobalOperationPrivilege() {
|
||||
final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege();
|
||||
// duplicate
|
||||
final GlobalOperationPrivilege privilege2 = new GlobalOperationPrivilege(privilege.getCategory(), privilege.getOperation(),
|
||||
new HashMap<>(privilege.getRaw()));
|
||||
final GlobalPrivileges globalPrivilege = new GlobalPrivileges(Arrays.asList(privilege, privilege2));
|
||||
assertThat(globalPrivilege.getPrivileges().size(), is(1));
|
||||
assertThat(globalPrivilege.getPrivileges().iterator().next(), is(privilege));
|
||||
}
|
||||
|
||||
public void testSameScopeGlobalOperationPrivilege() {
|
||||
final GlobalOperationPrivilege privilege = buildRandomGlobalScopedPrivilege();
|
||||
final GlobalOperationPrivilege sameOperationPrivilege = new GlobalOperationPrivilege(privilege.getCategory(),
|
||||
privilege.getOperation(), buildRandomGlobalScopedPrivilege().getRaw());
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> new GlobalPrivileges(Arrays.asList(privilege, sameOperationPrivilege)));
|
||||
assertThat(e.getMessage(), is("Different privileges for the same category and operation are not permitted"));
|
||||
}
|
||||
|
||||
private static GlobalOperationPrivilege buildRandomGlobalScopedPrivilege() {
|
||||
final Map<String, Object> privilege = new HashMap<>();
|
||||
for (int i = 0; i < randomIntBetween(1, 4); i++) {
|
||||
privilege.put(randomAlphaOfLength(2) + idCounter++, randomAlphaOfLengthBetween(1, 4));
|
||||
}
|
||||
return new GlobalOperationPrivilege("application", randomAlphaOfLength(2) + idCounter++, privilege);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue