Refactor cluster privileges and cluster permission (#45265) (#45442)

The current implementations make it difficult for
adding new privileges (example: a cluster privilege which is
more than cluster action-based and not exposed to the security
administrator). On the high level, we would like our cluster privilege
either:
- a named cluster privilege
  This corresponds to `cluster` field from the role descriptor
- or a configurable cluster privilege
  This corresponds to the `global` field from the role-descriptor and
allows a security administrator to configure them.

Some of the responsibilities like the merging of action based cluster privileges
are now pushed at cluster permission level. How to implement the predicate
(using Automaton) is being now enforced by cluster permission.

`ClusterPermission` helps in enforcing the cluster level access either by
performing checks against cluster action and optionally against a request.
It is a collection of one or more permission checks where if any of the checks
allow access then the permission allows access to a cluster action.

Implementations of cluster privilege must be able to provide information
regarding the predicates to the cluster permission so that can be enforced.
This is enforced by making implementations of cluster privilege aware of
cluster permission builder and provide a way to specify how the permission is
to be built for a given privilege.

This commit renames `ConditionalClusterPrivilege` to `ConfigurableClusterPrivilege`.
`ConfigurableClusterPrivilege` is a renderable cluster privilege exposed
as a `global` field in role descriptor.

Other than this there is a requirement where we would want to know if a cluster
permission is implied by another cluster-permission (`has-privileges`).
This is helpful in addressing queries related to privileges for a user.
This is not just simply checking of cluster permissions since we do not
have access to runtime information (like request object).
This refactoring does not try to address those scenarios.

Relates #44048
This commit is contained in:
Yogesh Gaikwad 2019-08-13 09:06:18 +10:00 committed by GitHub
parent 97efb6a403
commit 471d940c44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1116 additions and 630 deletions

View File

@ -36,7 +36,7 @@ import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessCo
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.user.User;
import java.util.ArrayList;
@ -199,7 +199,7 @@ public class CustomAuthorizationEngine implements AuthorizationEngine {
private GetUserPrivilegesResponse getUserPrivilegesResponse(boolean isSuperuser) {
final Set<String> cluster = isSuperuser ? Collections.singleton("ALL") : Collections.emptySet();
final Set<ConditionalClusterPrivilege> conditionalCluster = Collections.emptySet();
final Set<ConfigurableClusterPrivilege> conditionalCluster = Collections.emptySet();
final Set<GetUserPrivilegesResponse.Indices> indices = isSuperuser ? Collections.singleton(new Indices(Collections.singleton("*"),
Collections.singleton("*"), Collections.emptySet(), Collections.emptySet(), true)) : Collections.emptySet();

View File

@ -200,8 +200,8 @@ import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExceptExpression;
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.FieldExpression;
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.RoleMapperExpression;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.transport.netty4.SecurityNetty4Transport;
import org.elasticsearch.xpack.core.slm.SnapshotLifecycleMetadata;
import org.elasticsearch.xpack.core.slm.action.DeleteSnapshotLifecycleAction;
@ -476,9 +476,9 @@ public class XPackClientPlugin extends Plugin implements ActionPlugin, NetworkPl
new NamedWriteableRegistry.Entry(NamedDiff.class, TokenMetaData.TYPE, TokenMetaData::readDiffFrom),
new NamedWriteableRegistry.Entry(XPackFeatureSet.Usage.class, XPackField.SECURITY, SecurityFeatureSetUsage::new),
// security : conditional privileges
new NamedWriteableRegistry.Entry(ConditionalClusterPrivilege.class,
ConditionalClusterPrivileges.ManageApplicationPrivileges.WRITEABLE_NAME,
ConditionalClusterPrivileges.ManageApplicationPrivileges::createFrom),
new NamedWriteableRegistry.Entry(ConfigurableClusterPrivilege.class,
ConfigurableClusterPrivileges.ManageApplicationPrivileges.WRITEABLE_NAME,
ConfigurableClusterPrivileges.ManageApplicationPrivileges::createFrom),
// security : role-mappings
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, AllExpression.NAME, AllExpression::new),
new NamedWriteableRegistry.Entry(RoleMapperExpression.class, AnyExpression.NAME, AnyExpression::new),

View File

@ -16,8 +16,8 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
import java.io.IOException;
@ -36,7 +36,7 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
private String name;
private String[] clusterPrivileges = Strings.EMPTY_ARRAY;
private ConditionalClusterPrivilege[] conditionalClusterPrivileges = ConditionalClusterPrivileges.EMPTY_ARRAY;
private ConfigurableClusterPrivilege[] configurableClusterPrivileges = ConfigurableClusterPrivileges.EMPTY_ARRAY;
private List<RoleDescriptor.IndicesPrivileges> indicesPrivileges = new ArrayList<>();
private List<RoleDescriptor.ApplicationResourcePrivileges> applicationPrivileges = new ArrayList<>();
private String[] runAs = Strings.EMPTY_ARRAY;
@ -54,7 +54,7 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
}
if (in.getVersion().onOrAfter(Version.V_6_4_0)) {
applicationPrivileges = in.readList(RoleDescriptor.ApplicationResourcePrivileges::new);
conditionalClusterPrivileges = ConditionalClusterPrivileges.readArray(in);
configurableClusterPrivileges = ConfigurableClusterPrivileges.readArray(in);
}
runAs = in.readStringArray();
refreshPolicy = RefreshPolicy.readFrom(in);
@ -101,8 +101,8 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
this.clusterPrivileges = clusterPrivileges;
}
void conditionalCluster(ConditionalClusterPrivilege... conditionalClusterPrivileges) {
this.conditionalClusterPrivileges = conditionalClusterPrivileges;
void conditionalCluster(ConfigurableClusterPrivilege... configurableClusterPrivileges) {
this.configurableClusterPrivileges = configurableClusterPrivileges;
}
void addIndex(RoleDescriptor.IndicesPrivileges... privileges) {
@ -164,8 +164,8 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
return Collections.unmodifiableList(applicationPrivileges);
}
public ConditionalClusterPrivilege[] conditionalClusterPrivileges() {
return conditionalClusterPrivileges;
public ConfigurableClusterPrivilege[] conditionalClusterPrivileges() {
return configurableClusterPrivileges;
}
public String[] runAs() {
@ -187,7 +187,7 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
}
if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
out.writeList(applicationPrivileges);
ConditionalClusterPrivileges.writeArray(out, this.conditionalClusterPrivileges);
ConfigurableClusterPrivileges.writeArray(out, this.configurableClusterPrivileges);
}
out.writeStringArray(runAs);
refreshPolicy.writeTo(out);
@ -199,7 +199,7 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
clusterPrivileges,
indicesPrivileges.toArray(new RoleDescriptor.IndicesPrivileges[indicesPrivileges.size()]),
applicationPrivileges.toArray(new RoleDescriptor.ApplicationResourcePrivileges[applicationPrivileges.size()]),
conditionalClusterPrivileges,
configurableClusterPrivileges,
runAs,
metadata,
Collections.emptyMap());

View File

@ -16,8 +16,8 @@ import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import java.io.IOException;
import java.util.Collection;
@ -33,7 +33,7 @@ import java.util.stream.Collectors;
public final class GetUserPrivilegesResponse extends ActionResponse {
private Set<String> cluster;
private Set<ConditionalClusterPrivilege> conditionalCluster;
private Set<ConfigurableClusterPrivilege> configurableClusterPrivileges;
private Set<Indices> index;
private Set<RoleDescriptor.ApplicationResourcePrivileges> application;
private Set<String> runAs;
@ -41,18 +41,18 @@ public final class GetUserPrivilegesResponse extends ActionResponse {
public GetUserPrivilegesResponse(StreamInput in) throws IOException {
super(in);
cluster = Collections.unmodifiableSet(in.readSet(StreamInput::readString));
conditionalCluster = Collections.unmodifiableSet(in.readSet(ConditionalClusterPrivileges.READER));
configurableClusterPrivileges = Collections.unmodifiableSet(in.readSet(ConfigurableClusterPrivileges.READER));
index = Collections.unmodifiableSet(in.readSet(Indices::new));
application = Collections.unmodifiableSet(in.readSet(RoleDescriptor.ApplicationResourcePrivileges::new));
runAs = Collections.unmodifiableSet(in.readSet(StreamInput::readString));
}
public GetUserPrivilegesResponse(Set<String> cluster, Set<ConditionalClusterPrivilege> conditionalCluster,
public GetUserPrivilegesResponse(Set<String> cluster, Set<ConfigurableClusterPrivilege> conditionalCluster,
Set<Indices> index,
Set<RoleDescriptor.ApplicationResourcePrivileges> application,
Set<String> runAs) {
this.cluster = Collections.unmodifiableSet(cluster);
this.conditionalCluster = Collections.unmodifiableSet(conditionalCluster);
this.configurableClusterPrivileges = Collections.unmodifiableSet(conditionalCluster);
this.index = Collections.unmodifiableSet(index);
this.application = Collections.unmodifiableSet(application);
this.runAs = Collections.unmodifiableSet(runAs);
@ -62,8 +62,8 @@ public final class GetUserPrivilegesResponse extends ActionResponse {
return cluster;
}
public Set<ConditionalClusterPrivilege> getConditionalClusterPrivileges() {
return conditionalCluster;
public Set<ConfigurableClusterPrivilege> getConditionalClusterPrivileges() {
return configurableClusterPrivileges;
}
public Set<Indices> getIndexPrivileges() {
@ -81,7 +81,7 @@ public final class GetUserPrivilegesResponse extends ActionResponse {
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeCollection(cluster, StreamOutput::writeString);
out.writeCollection(conditionalCluster, ConditionalClusterPrivileges.WRITER);
out.writeCollection(configurableClusterPrivileges, ConfigurableClusterPrivileges.WRITER);
out.writeCollection(index);
out.writeCollection(application);
out.writeCollection(runAs, StreamOutput::writeString);
@ -97,7 +97,7 @@ public final class GetUserPrivilegesResponse extends ActionResponse {
}
final GetUserPrivilegesResponse that = (GetUserPrivilegesResponse) other;
return Objects.equals(cluster, that.cluster) &&
Objects.equals(conditionalCluster, that.conditionalCluster) &&
Objects.equals(configurableClusterPrivileges, that.configurableClusterPrivileges) &&
Objects.equals(index, that.index) &&
Objects.equals(application, that.application) &&
Objects.equals(runAs, that.runAs);
@ -105,7 +105,7 @@ public final class GetUserPrivilegesResponse extends ActionResponse {
@Override
public int hashCode() {
return Objects.hash(cluster, conditionalCluster, index, application, runAs);
return Objects.hash(cluster, configurableClusterPrivileges, index, application, runAs);
}
/**

View File

@ -24,8 +24,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.support.Validation;
import org.elasticsearch.xpack.core.security.xcontent.XContentUtils;
@ -49,7 +49,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
private final String name;
private final String[] clusterPrivileges;
private final ConditionalClusterPrivilege[] conditionalClusterPrivileges;
private final ConfigurableClusterPrivilege[] configurableClusterPrivileges;
private final IndicesPrivileges[] indicesPrivileges;
private final ApplicationResourcePrivileges[] applicationPrivileges;
private final String[] runAs;
@ -65,7 +65,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
/**
* @deprecated Use {@link #RoleDescriptor(String, String[], IndicesPrivileges[], ApplicationResourcePrivileges[],
* ConditionalClusterPrivilege[], String[], Map, Map)}
* ConfigurableClusterPrivilege[], String[], Map, Map)}
*/
@Deprecated
public RoleDescriptor(String name,
@ -78,7 +78,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
/**
* @deprecated Use {@link #RoleDescriptor(String, String[], IndicesPrivileges[], ApplicationResourcePrivileges[],
* ConditionalClusterPrivilege[], String[], Map, Map)}
* ConfigurableClusterPrivilege[], String[], Map, Map)}
*/
@Deprecated
public RoleDescriptor(String name,
@ -94,14 +94,14 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
@Nullable String[] clusterPrivileges,
@Nullable IndicesPrivileges[] indicesPrivileges,
@Nullable ApplicationResourcePrivileges[] applicationPrivileges,
@Nullable ConditionalClusterPrivilege[] conditionalClusterPrivileges,
@Nullable ConfigurableClusterPrivilege[] configurableClusterPrivileges,
@Nullable String[] runAs,
@Nullable Map<String, Object> metadata,
@Nullable Map<String, Object> transientMetadata) {
this.name = name;
this.clusterPrivileges = clusterPrivileges != null ? clusterPrivileges : Strings.EMPTY_ARRAY;
this.conditionalClusterPrivileges = conditionalClusterPrivileges != null
? conditionalClusterPrivileges : ConditionalClusterPrivileges.EMPTY_ARRAY;
this.configurableClusterPrivileges = configurableClusterPrivileges != null
? configurableClusterPrivileges : ConfigurableClusterPrivileges.EMPTY_ARRAY;
this.indicesPrivileges = indicesPrivileges != null ? indicesPrivileges : IndicesPrivileges.NONE;
this.applicationPrivileges = applicationPrivileges != null ? applicationPrivileges : ApplicationResourcePrivileges.NONE;
this.runAs = runAs != null ? runAs : Strings.EMPTY_ARRAY;
@ -121,13 +121,12 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
this.runAs = in.readStringArray();
this.metadata = in.readMap();
this.transientMetadata = in.readMap();
if (in.getVersion().onOrAfter(Version.V_6_4_0)) {
this.applicationPrivileges = in.readArray(ApplicationResourcePrivileges::new, ApplicationResourcePrivileges[]::new);
this.conditionalClusterPrivileges = ConditionalClusterPrivileges.readArray(in);
this.configurableClusterPrivileges = ConfigurableClusterPrivileges.readArray(in);
} else {
this.applicationPrivileges = ApplicationResourcePrivileges.NONE;
this.conditionalClusterPrivileges = ConditionalClusterPrivileges.EMPTY_ARRAY;
this.configurableClusterPrivileges = ConfigurableClusterPrivileges.EMPTY_ARRAY;
}
}
@ -139,8 +138,8 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
return this.clusterPrivileges;
}
public ConditionalClusterPrivilege[] getConditionalClusterPrivileges() {
return this.conditionalClusterPrivileges;
public ConfigurableClusterPrivilege[] getConditionalClusterPrivileges() {
return this.configurableClusterPrivileges;
}
public IndicesPrivileges[] getIndicesPrivileges() {
@ -172,7 +171,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
StringBuilder sb = new StringBuilder("Role[");
sb.append("name=").append(name);
sb.append(", cluster=[").append(Strings.arrayToCommaDelimitedString(clusterPrivileges));
sb.append("], global=[").append(Strings.arrayToCommaDelimitedString(conditionalClusterPrivileges));
sb.append("], global=[").append(Strings.arrayToCommaDelimitedString(configurableClusterPrivileges));
sb.append("], indicesPrivileges=[");
for (IndicesPrivileges group : indicesPrivileges) {
sb.append(group.toString()).append(",");
@ -197,7 +196,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
if (!name.equals(that.name)) return false;
if (!Arrays.equals(clusterPrivileges, that.clusterPrivileges)) return false;
if (!Arrays.equals(conditionalClusterPrivileges, that.conditionalClusterPrivileges)) return false;
if (!Arrays.equals(configurableClusterPrivileges, that.configurableClusterPrivileges)) return false;
if (!Arrays.equals(indicesPrivileges, that.indicesPrivileges)) return false;
if (!Arrays.equals(applicationPrivileges, that.applicationPrivileges)) return false;
if (!metadata.equals(that.getMetadata())) return false;
@ -208,7 +207,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
public int hashCode() {
int result = name.hashCode();
result = 31 * result + Arrays.hashCode(clusterPrivileges);
result = 31 * result + Arrays.hashCode(conditionalClusterPrivileges);
result = 31 * result + Arrays.hashCode(configurableClusterPrivileges);
result = 31 * result + Arrays.hashCode(indicesPrivileges);
result = 31 * result + Arrays.hashCode(applicationPrivileges);
result = 31 * result + Arrays.hashCode(runAs);
@ -235,9 +234,9 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
public XContentBuilder toXContent(XContentBuilder builder, Params params, boolean docCreation) throws IOException {
builder.startObject();
builder.array(Fields.CLUSTER.getPreferredName(), clusterPrivileges);
if (conditionalClusterPrivileges.length != 0) {
if (configurableClusterPrivileges.length != 0) {
builder.field(Fields.GLOBAL.getPreferredName());
ConditionalClusterPrivileges.toXContent(builder, params, Arrays.asList(conditionalClusterPrivileges));
ConfigurableClusterPrivileges.toXContent(builder, params, Arrays.asList(configurableClusterPrivileges));
}
builder.array(Fields.INDICES.getPreferredName(), (Object[]) indicesPrivileges);
builder.array(Fields.APPLICATIONS.getPreferredName(), (Object[]) applicationPrivileges);
@ -266,7 +265,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
out.writeMap(transientMetadata);
if (out.getVersion().onOrAfter(Version.V_6_4_0)) {
out.writeArray(ApplicationResourcePrivileges::write, applicationPrivileges);
ConditionalClusterPrivileges.writeArray(out, getConditionalClusterPrivileges());
ConfigurableClusterPrivileges.writeArray(out, getConditionalClusterPrivileges());
}
}
@ -298,7 +297,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
String currentFieldName = null;
IndicesPrivileges[] indicesPrivileges = null;
String[] clusterPrivileges = null;
List<ConditionalClusterPrivilege> conditionalClusterPrivileges = Collections.emptyList();
List<ConfigurableClusterPrivilege> configurableClusterPrivileges = Collections.emptyList();
ApplicationResourcePrivileges[] applicationPrivileges = null;
String[] runAsUsers = null;
Map<String, Object> metadata = null;
@ -316,7 +315,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
|| Fields.APPLICATION.match(currentFieldName, parser.getDeprecationHandler())) {
applicationPrivileges = parseApplicationPrivileges(name, parser);
} else if (Fields.GLOBAL.match(currentFieldName, parser.getDeprecationHandler())) {
conditionalClusterPrivileges = ConditionalClusterPrivileges.parse(parser);
configurableClusterPrivileges = ConfigurableClusterPrivileges.parse(parser);
} else if (Fields.METADATA.match(currentFieldName, parser.getDeprecationHandler())) {
if (token != XContentParser.Token.START_OBJECT) {
throw new ElasticsearchParseException(
@ -337,7 +336,7 @@ public class RoleDescriptor implements ToXContentObject, Writeable {
}
}
return new RoleDescriptor(name, clusterPrivileges, indicesPrivileges, applicationPrivileges,
conditionalClusterPrivileges.toArray(new ConditionalClusterPrivilege[conditionalClusterPrivileges.size()]), runAsUsers,
configurableClusterPrivileges.toArray(new ConfigurableClusterPrivilege[configurableClusterPrivileges.size()]), runAsUsers,
metadata, null);
}

View File

@ -5,121 +5,196 @@
*/
package org.elasticsearch.xpack.core.security.authz.permission;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* A permission that is based on privileges for cluster wide actions, with the optional ability to inspect the request object
*/
public abstract class ClusterPermission {
private final ClusterPrivilege privilege;
public class ClusterPermission {
public static final ClusterPermission NONE = new ClusterPermission(Collections.emptySet(), Collections.emptyList());
ClusterPermission(ClusterPrivilege privilege) {
this.privilege = privilege;
private final Set<ClusterPrivilege> clusterPrivileges;
private final List<PermissionCheck> checks;
private ClusterPermission(final Set<ClusterPrivilege> clusterPrivileges,
final List<PermissionCheck> checks) {
this.clusterPrivileges = Collections.unmodifiableSet(clusterPrivileges);
this.checks = Collections.unmodifiableList(checks);
}
public ClusterPrivilege privilege() {
return privilege;
}
public abstract boolean check(String action, TransportRequest request);
public boolean grants(ClusterPrivilege clusterPrivilege) {
return Operations.subsetOf(clusterPrivilege.getAutomaton(), this.privilege().getAutomaton());
}
public abstract List<Tuple<ClusterPrivilege, ConditionalClusterPrivilege>> privileges();
/**
* A permission that is based solely on cluster privileges and does not consider request state
* Checks permission to a cluster action for a given request.
*
* @param action cluster action
* @param request {@link TransportRequest}
* @return {@code true} if the access is allowed else returns {@code false}
*/
public static class SimpleClusterPermission extends ClusterPermission {
public boolean check(final String action, final TransportRequest request) {
return checks.stream().anyMatch(permission -> permission.check(action, request));
}
public static final SimpleClusterPermission NONE = new SimpleClusterPermission(ClusterPrivilege.NONE);
/**
* Checks if the specified {@link ClusterPermission}'s actions are implied by this {@link ClusterPermission}
*
* @param otherClusterPermission {@link ClusterPermission}
* @return {@code true} if the specified cluster permissions actions are implied by this cluster permission else returns {@code false}
*/
public boolean implies(final ClusterPermission otherClusterPermission) {
if (otherClusterPermission.checks.isEmpty()) {
return true;
} else {
for (PermissionCheck otherPermissionCheck : otherClusterPermission.checks) {
boolean isImplied = this.checks.stream().anyMatch(thisPermissionCheck -> thisPermissionCheck.implies(otherPermissionCheck));
if (isImplied == false) {
return false;
}
}
return true;
}
}
private final Predicate<String> predicate;
public Set<ClusterPrivilege> privileges() {
return clusterPrivileges;
}
SimpleClusterPermission(ClusterPrivilege privilege) {
super(privilege);
this.predicate = privilege.predicate();
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final Set<ClusterPrivilege> clusterPrivileges = new HashSet<>();
private final List<Automaton> actionAutomatons = new ArrayList<>();
private final List<PermissionCheck> permissionChecks = new ArrayList<>();
public Builder add(final ClusterPrivilege clusterPrivilege, final Set<String> allowedActionPatterns,
final Set<String> excludeActionPatterns) {
this.clusterPrivileges.add(clusterPrivilege);
if (allowedActionPatterns.isEmpty() && excludeActionPatterns.isEmpty()) {
this.actionAutomatons.add(Automatons.EMPTY);
} else {
final Automaton allowedAutomaton = Automatons.patterns(allowedActionPatterns);
final Automaton excludedAutomaton = Automatons.patterns(excludeActionPatterns);
this.actionAutomatons.add(Automatons.minusAndMinimize(allowedAutomaton, excludedAutomaton));
}
return this;
}
@Override
public boolean check(String action, TransportRequest request) {
return predicate.test(action);
public Builder add(final ConfigurableClusterPrivilege configurableClusterPrivilege, final Predicate<String> actionPredicate,
final Predicate<TransportRequest> requestPredicate) {
return add(configurableClusterPrivilege, new ActionRequestPredicatePermissionCheck(configurableClusterPrivilege,
actionPredicate,
requestPredicate));
}
@Override
public List<Tuple<ClusterPrivilege, ConditionalClusterPrivilege>> privileges() {
return Collections.singletonList(new Tuple<>(super.privilege, null));
public Builder add(final ClusterPrivilege clusterPrivilege, final PermissionCheck permissionCheck) {
this.clusterPrivileges.add(clusterPrivilege);
this.permissionChecks.add(permissionCheck);
return this;
}
public ClusterPermission build() {
if (clusterPrivileges.isEmpty()) {
return NONE;
}
List<PermissionCheck> checks = this.permissionChecks;
if (false == actionAutomatons.isEmpty()) {
final Automaton mergedAutomaton = Automatons.unionAndMinimize(this.actionAutomatons);
checks = new ArrayList<>(this.permissionChecks.size() + 1);
checks.add(new AutomatonPermissionCheck(mergedAutomaton));
checks.addAll(this.permissionChecks);
}
return new ClusterPermission(this.clusterPrivileges, checks);
}
}
/**
* A permission that makes use of both cluster privileges and request inspection
* Evaluates whether the cluster actions (optionally for a given request)
* is permitted by this permission.
*/
public static class ConditionalClusterPermission extends ClusterPermission {
private final ConditionalClusterPrivilege conditionalPrivilege;
public interface PermissionCheck {
/**
* Checks permission to a cluster action for a given request.
*
* @param action action name
* @param request {@link TransportRequest}
* @return {@code true} if the specified action for given request is allowed else returns {@code false}
*/
boolean check(String action, TransportRequest request);
public ConditionalClusterPermission(ConditionalClusterPrivilege conditionalPrivilege) {
super(conditionalPrivilege.getPrivilege());
this.conditionalPrivilege = conditionalPrivilege;
/**
* Checks whether specified {@link PermissionCheck} is implied by this {@link PermissionCheck}.<br>
* This is important method to be considered during implementation as it compares {@link PermissionCheck}s.
* If {@code permissionCheck.implies(otherPermissionCheck)}, that means all the actions allowed by {@code otherPermissionCheck}
* are also allowed by {@code permissionCheck}, irrespective of the request structure.
*
* @param otherPermissionCheck {@link PermissionCheck}
* @return {@code true} if the specified permission is implied by this {@link PermissionCheck} else
* returns {@code false}
*/
boolean implies(PermissionCheck otherPermissionCheck);
}
// Automaton based permission check
private static class AutomatonPermissionCheck implements PermissionCheck {
private final Automaton automaton;
private final Predicate<String> actionPredicate;
AutomatonPermissionCheck(final Automaton automaton) {
this.automaton = automaton;
this.actionPredicate = Automatons.predicate(automaton);
}
@Override
public boolean check(String action, TransportRequest request) {
return super.privilege.predicate().test(action) && conditionalPrivilege.getRequestPredicate().test(request);
public boolean check(final String action, final TransportRequest request) {
return actionPredicate.test(action);
}
@Override
public List<Tuple<ClusterPrivilege, ConditionalClusterPrivilege>> privileges() {
return Collections.singletonList(new Tuple<>(super.privilege, conditionalPrivilege));
public boolean implies(final PermissionCheck permissionCheck) {
if (permissionCheck instanceof AutomatonPermissionCheck) {
return Operations.subsetOf(((AutomatonPermissionCheck) permissionCheck).automaton, this.automaton);
}
return false;
}
}
/**
* A permission that composes a number of other cluster permissions
*/
public static class CompositeClusterPermission extends ClusterPermission {
private final Collection<ClusterPermission> children;
// action and request based permission check
private static class ActionRequestPredicatePermissionCheck implements PermissionCheck {
private final ClusterPrivilege clusterPrivilege;
final Predicate<String> actionPredicate;
final Predicate<TransportRequest> requestPredicate;
public CompositeClusterPermission(Collection<ClusterPermission> children) {
super(buildPrivilege(children));
this.children = children;
}
private static ClusterPrivilege buildPrivilege(Collection<ClusterPermission> children) {
final Set<String> names = children.stream()
.map(ClusterPermission::privilege)
.map(ClusterPrivilege::name)
.flatMap(Set::stream)
.collect(Collectors.toSet());
return ClusterPrivilege.get(names);
ActionRequestPredicatePermissionCheck(final ClusterPrivilege clusterPrivilege, final Predicate<String> actionPredicate,
final Predicate<TransportRequest> requestPredicate) {
this.clusterPrivilege = clusterPrivilege;
this.actionPredicate = actionPredicate;
this.requestPredicate = requestPredicate;
}
@Override
public List<Tuple<ClusterPrivilege, ConditionalClusterPrivilege>> privileges() {
return children.stream().map(ClusterPermission::privileges).flatMap(List::stream).collect(Collectors.toList());
public boolean check(final String action, final TransportRequest request) {
return actionPredicate.test(action) && requestPredicate.test(request);
}
@Override
public boolean check(String action, TransportRequest request) {
return children.stream().anyMatch(p -> p.check(action, request));
}
@Override
public boolean grants(ClusterPrivilege clusterPrivilege) {
return children.stream().anyMatch(p -> p.grants(clusterPrivilege));
public boolean implies(final PermissionCheck permissionCheck) {
if (permissionCheck instanceof ActionRequestPredicatePermissionCheck) {
final ActionRequestPredicatePermissionCheck otherCheck = (ActionRequestPredicatePermissionCheck) permissionCheck;
return this.clusterPrivilege.equals(otherCheck.clusterPrivilege);
}
return false;
}
}
}

View File

@ -17,7 +17,8 @@ import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessCo
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
@ -137,7 +138,7 @@ public class Role {
* @return {@code true} if cluster privilege is allowed else returns {@code false}
*/
public boolean grants(ClusterPrivilege clusterPrivilege) {
return cluster.grants(clusterPrivilege);
return cluster.implies(clusterPrivilege.buildPermission(ClusterPermission.builder()).build());
}
/**
@ -184,7 +185,7 @@ public class Role {
public static class Builder {
private final String[] names;
private ClusterPermission cluster = ClusterPermission.SimpleClusterPermission.NONE;
private ClusterPermission cluster = ClusterPermission.NONE;
private RunAsPermission runAs = RunAsPermission.NONE;
private List<IndicesPermission.Group> groups = new ArrayList<>();
private List<Tuple<ApplicationPrivilege, Set<String>>> applicationPrivs = new ArrayList<>();
@ -209,30 +210,18 @@ public class Role {
}
}
public Builder cluster(Set<String> privilegeNames, Iterable<ConditionalClusterPrivilege> conditionalClusterPrivileges) {
public Builder cluster(Set<String> privilegeNames, Iterable<ConfigurableClusterPrivilege> configurableClusterPrivileges) {
ClusterPermission.Builder builder = ClusterPermission.builder();
List<ClusterPermission> clusterPermissions = new ArrayList<>();
if (privilegeNames.isEmpty() == false) {
clusterPermissions.add(new ClusterPermission.SimpleClusterPermission(ClusterPrivilege.get(privilegeNames)));
for (String name : privilegeNames) {
builder = ClusterPrivilegeResolver.resolve(name).buildPermission(builder);
}
}
for (ConditionalClusterPrivilege ccp : conditionalClusterPrivileges) {
clusterPermissions.add(new ClusterPermission.ConditionalClusterPermission(ccp));
for (ConfigurableClusterPrivilege ccp : configurableClusterPrivileges) {
builder = ccp.buildPermission(builder);
}
if (clusterPermissions.isEmpty()) {
this.cluster = ClusterPermission.SimpleClusterPermission.NONE;
} else if (clusterPermissions.size() == 1) {
this.cluster = clusterPermissions.get(0);
} else {
this.cluster = new ClusterPermission.CompositeClusterPermission(clusterPermissions);
}
return this;
}
/**
* @deprecated Use {@link #cluster(Set, Iterable)}
*/
@Deprecated
public Builder cluster(ClusterPrivilege privilege) {
cluster = new ClusterPermission.SimpleClusterPermission(privilege);
this.cluster = builder.build();
return this;
}

View File

@ -0,0 +1,66 @@
/*
*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*
*/
package org.elasticsearch.xpack.core.security.authz.privilege;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import java.util.Collections;
import java.util.Set;
/**
* A {@link NamedClusterPrivilege} that can be used to define an access to cluster level actions.
*/
public class ActionClusterPrivilege implements NamedClusterPrivilege {
private final String name;
private final Set<String> allowedActionPatterns;
private final Set<String> excludedActionPatterns;
/**
* Constructor for {@link ActionClusterPrivilege} defining what cluster actions are accessible for the user with this privilege.
*
* @param name name for the cluster privilege
* @param allowedActionPatterns a set of cluster action patterns that are allowed for the user with this privilege.
*/
public ActionClusterPrivilege(final String name, final Set<String> allowedActionPatterns) {
this(name, allowedActionPatterns, Collections.emptySet());
}
/**
* Constructor for {@link ActionClusterPrivilege} that defines what cluster actions are accessible for the
* user with this privilege after excluding the action patterns {@code excludedActionPatterns} from the allowed action patterns
* {@code allowedActionPatterns}
*
* @param name name for the cluster privilege
* @param allowedActionPatterns a set of cluster action patterns
* @param excludedActionPatterns a set of cluster action patterns
*/
public ActionClusterPrivilege(final String name, final Set<String> allowedActionPatterns, final Set<String> excludedActionPatterns) {
this.name = name;
this.allowedActionPatterns = allowedActionPatterns;
this.excludedActionPatterns = excludedActionPatterns;
}
@Override
public String name() {
return name;
}
public Set<String> getAllowedActionPatterns() {
return allowedActionPatterns;
}
public Set<String> getExcludedActionPatterns() {
return excludedActionPatterns;
}
@Override
public ClusterPermission.Builder buildPermission(final ClusterPermission.Builder builder) {
return builder.add(this, allowedActionPatterns, excludedActionPatterns);
}
}

View File

@ -5,190 +5,16 @@
*/
package org.elasticsearch.xpack.core.security.authz.privilege;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction;
import org.elasticsearch.xpack.core.ilm.action.GetStatusAction;
import org.elasticsearch.xpack.core.ilm.action.StartILMAction;
import org.elasticsearch.xpack.core.ilm.action.StopILMAction;
import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction;
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.slm.action.GetSnapshotLifecycleAction;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.core.security.support.Automatons.minusAndMinimize;
import static org.elasticsearch.xpack.core.security.support.Automatons.patterns;
public final class ClusterPrivilege extends Privilege {
// shared automatons
private static final Automaton MANAGE_SECURITY_AUTOMATON = patterns("cluster:admin/xpack/security/*");
private static final Automaton MANAGE_SAML_AUTOMATON = patterns("cluster:admin/xpack/security/saml/*",
InvalidateTokenAction.NAME, RefreshTokenAction.NAME);
private static final Automaton MANAGE_OIDC_AUTOMATON = patterns("cluster:admin/xpack/security/oidc/*");
private static final Automaton MANAGE_TOKEN_AUTOMATON = patterns("cluster:admin/xpack/security/token/*");
private static final Automaton MANAGE_API_KEY_AUTOMATON = patterns("cluster:admin/xpack/security/api_key/*");
private static final Automaton MONITOR_AUTOMATON = patterns("cluster:monitor/*");
private static final Automaton MONITOR_ML_AUTOMATON = patterns("cluster:monitor/xpack/ml/*");
private static final Automaton MONITOR_DATA_FRAME_AUTOMATON = patterns("cluster:monitor/data_frame/*");
private static final Automaton MONITOR_WATCHER_AUTOMATON = patterns("cluster:monitor/xpack/watcher/*");
private static final Automaton MONITOR_ROLLUP_AUTOMATON = patterns("cluster:monitor/xpack/rollup/*");
private static final Automaton ALL_CLUSTER_AUTOMATON = patterns("cluster:*", "indices:admin/template/*");
private static final Automaton MANAGE_AUTOMATON = minusAndMinimize(ALL_CLUSTER_AUTOMATON, MANAGE_SECURITY_AUTOMATON);
private static final Automaton MANAGE_ML_AUTOMATON = patterns("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*");
private static final Automaton MANAGE_DATA_FRAME_AUTOMATON = patterns("cluster:admin/data_frame/*", "cluster:monitor/data_frame/*");
private static final Automaton MANAGE_WATCHER_AUTOMATON = patterns("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*");
private static final Automaton TRANSPORT_CLIENT_AUTOMATON = patterns("cluster:monitor/nodes/liveness", "cluster:monitor/state");
private static final Automaton MANAGE_IDX_TEMPLATE_AUTOMATON = patterns("indices:admin/template/*");
private static final Automaton MANAGE_INGEST_PIPELINE_AUTOMATON = patterns("cluster:admin/ingest/pipeline/*");
private static final Automaton MANAGE_ROLLUP_AUTOMATON = patterns("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*");
private static final Automaton MANAGE_CCR_AUTOMATON =
patterns("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME);
private static final Automaton CREATE_SNAPSHOT_AUTOMATON = patterns(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*",
GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME);
private static final Automaton READ_CCR_AUTOMATON = patterns(ClusterStateAction.NAME, HasPrivilegesAction.NAME);
private static final Automaton MANAGE_ILM_AUTOMATON = patterns("cluster:admin/ilm/*");
private static final Automaton READ_ILM_AUTOMATON = patterns(GetLifecycleAction.NAME, GetStatusAction.NAME);
private static final Automaton MANAGE_SLM_AUTOMATON =
patterns("cluster:admin/slm/*", StartILMAction.NAME, StopILMAction.NAME, GetStatusAction.NAME);
private static final Automaton READ_SLM_AUTOMATON = patterns(GetSnapshotLifecycleAction.NAME, GetStatusAction.NAME);
public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY);
public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON);
public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", MONITOR_AUTOMATON);
public static final ClusterPrivilege MONITOR_ML = new ClusterPrivilege("monitor_ml", MONITOR_ML_AUTOMATON);
public static final ClusterPrivilege MONITOR_DATA_FRAME =
new ClusterPrivilege("monitor_data_frame_transforms", MONITOR_DATA_FRAME_AUTOMATON);
public static final ClusterPrivilege MONITOR_WATCHER = new ClusterPrivilege("monitor_watcher", MONITOR_WATCHER_AUTOMATON);
public static final ClusterPrivilege MONITOR_ROLLUP = new ClusterPrivilege("monitor_rollup", MONITOR_ROLLUP_AUTOMATON);
public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage", MANAGE_AUTOMATON);
public static final ClusterPrivilege MANAGE_ML = new ClusterPrivilege("manage_ml", MANAGE_ML_AUTOMATON);
public static final ClusterPrivilege MANAGE_DATA_FRAME =
new ClusterPrivilege("manage_data_frame_transforms", MANAGE_DATA_FRAME_AUTOMATON);
public static final ClusterPrivilege MANAGE_TOKEN = new ClusterPrivilege("manage_token", MANAGE_TOKEN_AUTOMATON);
public static final ClusterPrivilege MANAGE_WATCHER = new ClusterPrivilege("manage_watcher", MANAGE_WATCHER_AUTOMATON);
public static final ClusterPrivilege MANAGE_ROLLUP = new ClusterPrivilege("manage_rollup", MANAGE_ROLLUP_AUTOMATON);
public static final ClusterPrivilege MANAGE_IDX_TEMPLATES =
new ClusterPrivilege("manage_index_templates", MANAGE_IDX_TEMPLATE_AUTOMATON);
public static final ClusterPrivilege MANAGE_INGEST_PIPELINES =
new ClusterPrivilege("manage_ingest_pipelines", MANAGE_INGEST_PIPELINE_AUTOMATON);
public static final ClusterPrivilege TRANSPORT_CLIENT = new ClusterPrivilege("transport_client", TRANSPORT_CLIENT_AUTOMATON);
public static final ClusterPrivilege MANAGE_SECURITY = new ClusterPrivilege("manage_security", MANAGE_SECURITY_AUTOMATON);
public static final ClusterPrivilege MANAGE_SAML = new ClusterPrivilege("manage_saml", MANAGE_SAML_AUTOMATON);
public static final ClusterPrivilege MANAGE_OIDC = new ClusterPrivilege("manage_oidc", MANAGE_OIDC_AUTOMATON);
public static final ClusterPrivilege MANAGE_API_KEY = new ClusterPrivilege("manage_api_key", MANAGE_API_KEY_AUTOMATON);
public static final ClusterPrivilege MANAGE_PIPELINE = new ClusterPrivilege("manage_pipeline", "cluster:admin/ingest/pipeline/*");
public static final ClusterPrivilege MANAGE_CCR = new ClusterPrivilege("manage_ccr", MANAGE_CCR_AUTOMATON);
public static final ClusterPrivilege READ_CCR = new ClusterPrivilege("read_ccr", READ_CCR_AUTOMATON);
public static final ClusterPrivilege CREATE_SNAPSHOT = new ClusterPrivilege("create_snapshot", CREATE_SNAPSHOT_AUTOMATON);
public static final ClusterPrivilege MANAGE_ILM = new ClusterPrivilege("manage_ilm", MANAGE_ILM_AUTOMATON);
public static final ClusterPrivilege READ_ILM = new ClusterPrivilege("read_ilm", READ_ILM_AUTOMATON);
public static final ClusterPrivilege MANAGE_SLM = new ClusterPrivilege("manage_slm", MANAGE_SLM_AUTOMATON);
public static final ClusterPrivilege READ_SLM = new ClusterPrivilege("read_slm", READ_SLM_AUTOMATON);
public static final Predicate<String> ACTION_MATCHER = ClusterPrivilege.ALL.predicate();
private static final Map<String, ClusterPrivilege> VALUES = MapBuilder.<String, ClusterPrivilege>newMapBuilder()
.put("none", NONE)
.put("all", ALL)
.put("monitor", MONITOR)
.put("monitor_ml", MONITOR_ML)
.put("monitor_data_frame_transforms", MONITOR_DATA_FRAME)
.put("monitor_watcher", MONITOR_WATCHER)
.put("monitor_rollup", MONITOR_ROLLUP)
.put("manage", MANAGE)
.put("manage_ml", MANAGE_ML)
.put("manage_data_frame_transforms", MANAGE_DATA_FRAME)
.put("manage_token", MANAGE_TOKEN)
.put("manage_watcher", MANAGE_WATCHER)
.put("manage_index_templates", MANAGE_IDX_TEMPLATES)
.put("manage_ingest_pipelines", MANAGE_INGEST_PIPELINES)
.put("transport_client", TRANSPORT_CLIENT)
.put("manage_security", MANAGE_SECURITY)
.put("manage_saml", MANAGE_SAML)
.put("manage_oidc", MANAGE_OIDC)
.put("manage_api_key", MANAGE_API_KEY)
.put("manage_pipeline", MANAGE_PIPELINE)
.put("manage_rollup", MANAGE_ROLLUP)
.put("manage_ccr", MANAGE_CCR)
.put("read_ccr", READ_CCR)
.put("create_snapshot", CREATE_SNAPSHOT)
.put("manage_ilm", MANAGE_ILM)
.put("read_ilm", READ_ILM)
.put("manage_slm", MANAGE_SLM)
.put("read_slm", READ_SLM)
.immutableMap();
private static final ConcurrentHashMap<Set<String>, ClusterPrivilege> CACHE = new ConcurrentHashMap<>();
private ClusterPrivilege(String name, String... patterns) {
super(name, patterns);
}
private ClusterPrivilege(String name, Automaton automaton) {
super(Collections.singleton(name), automaton);
}
private ClusterPrivilege(Set<String> name, Automaton automaton) {
super(name, automaton);
}
public static ClusterPrivilege get(Set<String> name) {
if (name == null || name.isEmpty()) {
return NONE;
}
return CACHE.computeIfAbsent(name, ClusterPrivilege::resolve);
}
private static ClusterPrivilege resolve(Set<String> name) {
final int size = name.size();
if (size == 0) {
throw new IllegalArgumentException("empty set should not be used");
}
Set<String> actions = new HashSet<>();
Set<Automaton> automata = new HashSet<>();
for (String part : name) {
part = part.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(part)) {
actions.add(actionToPattern(part));
} else {
ClusterPrivilege privilege = VALUES.get(part);
if (privilege != null && size == 1) {
return privilege;
} else if (privilege != null) {
automata.add(privilege.automaton);
} else {
throw new IllegalArgumentException("unknown cluster privilege [" + name + "]. a privilege must be either " +
"one of the predefined fixed cluster privileges [" +
Strings.collectionToCommaDelimitedString(VALUES.entrySet()) + "] or a pattern over one of the available " +
"cluster actions");
}
}
}
if (actions.isEmpty() == false) {
automata.add(patterns(actions));
}
return new ClusterPrivilege(name, Automatons.unionAndMinimize(automata));
}
public static Set<String> names() {
return Collections.unmodifiableSet(VALUES.keySet());
}
/**
* This interface represents a privilege that is used to control access to cluster level actions.
*/
public interface ClusterPrivilege {
/**
* Uses {@link ClusterPermission.Builder} to add predicate that later can be used to build a {@link ClusterPermission}.
* @param builder {@link ClusterPermission.Builder}
* @return an instance of {@link ClusterPermission.Builder}
*/
ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder);
}

View File

@ -0,0 +1,183 @@
/*
*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*
*/
package org.elasticsearch.xpack.core.security.authz.privilege;
import org.elasticsearch.action.admin.cluster.repositories.get.GetRepositoriesAction;
import org.elasticsearch.action.admin.cluster.snapshots.create.CreateSnapshotAction;
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsAction;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.core.ilm.action.GetLifecycleAction;
import org.elasticsearch.xpack.core.ilm.action.GetStatusAction;
import org.elasticsearch.xpack.core.ilm.action.StartILMAction;
import org.elasticsearch.xpack.core.ilm.action.StopILMAction;
import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction;
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction;
import org.elasticsearch.xpack.core.slm.action.GetSnapshotLifecycleAction;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Translates cluster privilege names into concrete implementations
*/
public class ClusterPrivilegeResolver {
// shared automatons
private static final Set<String> MANAGE_SECURITY_PATTERN = Collections.singleton("cluster:admin/xpack/security/*");
private static final Set<String> MANAGE_SAML_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/security/saml/*",
InvalidateTokenAction.NAME, RefreshTokenAction.NAME));
private static final Set<String> MANAGE_OIDC_PATTERN = Collections.singleton("cluster:admin/xpack/security/oidc/*");
private static final Set<String> MANAGE_TOKEN_PATTERN = Collections.singleton("cluster:admin/xpack/security/token/*");
private static final Set<String> MANAGE_API_KEY_PATTERN = Collections.singleton("cluster:admin/xpack/security/api_key/*");
private static final Set<String> MONITOR_PATTERN = Collections.singleton("cluster:monitor/*");
private static final Set<String> MONITOR_ML_PATTERN = Collections.singleton("cluster:monitor/xpack/ml/*");
private static final Set<String> MONITOR_DATA_FRAME_PATTERN = Collections.singleton("cluster:monitor/data_frame/*");
private static final Set<String> MONITOR_WATCHER_PATTERN = Collections.singleton("cluster:monitor/xpack/watcher/*");
private static final Set<String> MONITOR_ROLLUP_PATTERN = Collections.singleton("cluster:monitor/xpack/rollup/*");
private static final Set<String> ALL_CLUSTER_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:*", "indices:admin/template/*"));
private static final Set<String> MANAGE_ML_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/ml/*", "cluster:monitor/xpack/ml/*"));
private static final Set<String> MANAGE_DATA_FRAME_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/data_frame/*", "cluster:monitor/data_frame/*"));
private static final Set<String> MANAGE_WATCHER_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/watcher/*", "cluster:monitor/xpack/watcher/*"));
private static final Set<String> TRANSPORT_CLIENT_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:monitor/nodes/liveness", "cluster:monitor/state"));
private static final Set<String> MANAGE_IDX_TEMPLATE_PATTERN = Collections.singleton("indices:admin/template/*");
private static final Set<String> MANAGE_INGEST_PIPELINE_PATTERN = Collections.singleton("cluster:admin/ingest/pipeline/*");
private static final Set<String> MANAGE_ROLLUP_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet("cluster:admin/xpack/rollup/*", "cluster:monitor/xpack/rollup/*"));
private static final Set<String> MANAGE_CCR_PATTERN =
Collections.unmodifiableSet(Sets.newHashSet("cluster:admin/xpack/ccr/*", ClusterStateAction.NAME, HasPrivilegesAction.NAME));
private static final Set<String> CREATE_SNAPSHOT_PATTERN = Collections.unmodifiableSet(
Sets.newHashSet(CreateSnapshotAction.NAME, SnapshotsStatusAction.NAME + "*",
GetSnapshotsAction.NAME, SnapshotsStatusAction.NAME, GetRepositoriesAction.NAME));
private static final Set<String> READ_CCR_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(ClusterStateAction.NAME,
HasPrivilegesAction.NAME));
private static final Set<String> MANAGE_ILM_PATTERN = Collections.singleton("cluster:admin/ilm/*");
private static final Set<String> READ_ILM_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(GetLifecycleAction.NAME,
GetStatusAction.NAME));
private static final Set<String> MANAGE_SLM_PATTERN =
Collections.unmodifiableSet(Sets.newHashSet("cluster:admin/slm/*", StartILMAction.NAME, StopILMAction.NAME, GetStatusAction.NAME));
private static final Set<String> READ_SLM_PATTERN = Collections.unmodifiableSet(Sets.newHashSet(GetSnapshotLifecycleAction.NAME,
GetStatusAction.NAME));
public static final NamedClusterPrivilege NONE = new ActionClusterPrivilege("none", Collections.emptySet(), Collections.emptySet());
public static final NamedClusterPrivilege ALL = new ActionClusterPrivilege("all", ALL_CLUSTER_PATTERN);
public static final NamedClusterPrivilege MONITOR = new ActionClusterPrivilege("monitor", MONITOR_PATTERN);
public static final NamedClusterPrivilege MONITOR_ML = new ActionClusterPrivilege("monitor_ml", MONITOR_ML_PATTERN);
public static final NamedClusterPrivilege MONITOR_DATA_FRAME =
new ActionClusterPrivilege("monitor_data_frame_transforms", MONITOR_DATA_FRAME_PATTERN);
public static final NamedClusterPrivilege MONITOR_WATCHER = new ActionClusterPrivilege("monitor_watcher", MONITOR_WATCHER_PATTERN);
public static final NamedClusterPrivilege MONITOR_ROLLUP = new ActionClusterPrivilege("monitor_rollup", MONITOR_ROLLUP_PATTERN);
public static final NamedClusterPrivilege MANAGE = new ActionClusterPrivilege("manage",
ALL_CLUSTER_PATTERN, MANAGE_SECURITY_PATTERN);
public static final NamedClusterPrivilege MANAGE_ML = new ActionClusterPrivilege("manage_ml", MANAGE_ML_PATTERN);
public static final NamedClusterPrivilege MANAGE_DATA_FRAME =
new ActionClusterPrivilege("manage_data_frame_transforms", MANAGE_DATA_FRAME_PATTERN);
public static final NamedClusterPrivilege MANAGE_TOKEN = new ActionClusterPrivilege("manage_token", MANAGE_TOKEN_PATTERN);
public static final NamedClusterPrivilege MANAGE_WATCHER = new ActionClusterPrivilege("manage_watcher", MANAGE_WATCHER_PATTERN);
public static final NamedClusterPrivilege MANAGE_ROLLUP = new ActionClusterPrivilege("manage_rollup", MANAGE_ROLLUP_PATTERN);
public static final NamedClusterPrivilege MANAGE_IDX_TEMPLATES =
new ActionClusterPrivilege("manage_index_templates", MANAGE_IDX_TEMPLATE_PATTERN);
public static final NamedClusterPrivilege MANAGE_INGEST_PIPELINES =
new ActionClusterPrivilege("manage_ingest_pipelines", MANAGE_INGEST_PIPELINE_PATTERN);
public static final NamedClusterPrivilege TRANSPORT_CLIENT = new ActionClusterPrivilege("transport_client",
TRANSPORT_CLIENT_PATTERN);
public static final NamedClusterPrivilege MANAGE_SECURITY = new ActionClusterPrivilege("manage_security", MANAGE_SECURITY_PATTERN);
public static final NamedClusterPrivilege MANAGE_SAML = new ActionClusterPrivilege("manage_saml", MANAGE_SAML_PATTERN);
public static final NamedClusterPrivilege MANAGE_OIDC = new ActionClusterPrivilege("manage_oidc", MANAGE_OIDC_PATTERN);
public static final NamedClusterPrivilege MANAGE_API_KEY = new ActionClusterPrivilege("manage_api_key", MANAGE_API_KEY_PATTERN);
public static final NamedClusterPrivilege MANAGE_PIPELINE = new ActionClusterPrivilege("manage_pipeline",
Collections.singleton("cluster:admin/ingest/pipeline/*"));
public static final NamedClusterPrivilege MANAGE_CCR = new ActionClusterPrivilege("manage_ccr", MANAGE_CCR_PATTERN);
public static final NamedClusterPrivilege READ_CCR = new ActionClusterPrivilege("read_ccr", READ_CCR_PATTERN);
public static final NamedClusterPrivilege CREATE_SNAPSHOT = new ActionClusterPrivilege("create_snapshot", CREATE_SNAPSHOT_PATTERN);
public static final NamedClusterPrivilege MANAGE_ILM = new ActionClusterPrivilege("manage_ilm", MANAGE_ILM_PATTERN);
public static final NamedClusterPrivilege READ_ILM = new ActionClusterPrivilege("read_ilm", READ_ILM_PATTERN);
public static final NamedClusterPrivilege MANAGE_SLM = new ActionClusterPrivilege("manage_slm", MANAGE_SLM_PATTERN);
public static final NamedClusterPrivilege READ_SLM = new ActionClusterPrivilege("read_slm", READ_SLM_PATTERN);
private static final Map<String, NamedClusterPrivilege> VALUES = Collections.unmodifiableMap(
Stream.of(
NONE,
ALL,
MONITOR,
MONITOR_ML,
MONITOR_DATA_FRAME,
MONITOR_WATCHER,
MONITOR_ROLLUP,
MANAGE,
MANAGE_ML,
MANAGE_DATA_FRAME,
MANAGE_TOKEN,
MANAGE_WATCHER,
MANAGE_IDX_TEMPLATES,
MANAGE_INGEST_PIPELINES,
TRANSPORT_CLIENT,
MANAGE_SECURITY,
MANAGE_SAML,
MANAGE_OIDC,
MANAGE_API_KEY,
MANAGE_PIPELINE,
MANAGE_ROLLUP,
MANAGE_CCR,
READ_CCR,
CREATE_SNAPSHOT,
MANAGE_ILM,
READ_ILM,
MANAGE_SLM,
READ_SLM).collect(Collectors.toMap(cp -> cp.name(), cp -> cp)));
/**
* Resolves a {@link NamedClusterPrivilege} from a given name if it exists.
* If the name is a cluster action, then it converts the name to pattern and creates a {@link ActionClusterPrivilege}
*
* @param name either {@link ClusterPrivilegeResolver#names()} or cluster action {@link #isClusterAction(String)}
* @return instance of {@link NamedClusterPrivilege}
*/
public static NamedClusterPrivilege resolve(String name) {
name = Objects.requireNonNull(name).toLowerCase(Locale.ROOT);
if (isClusterAction(name)) {
return new ActionClusterPrivilege(name, Collections.singleton(actionToPattern(name)));
}
final NamedClusterPrivilege fixedPrivilege = VALUES.get(name);
if (fixedPrivilege != null) {
return fixedPrivilege;
}
throw new IllegalArgumentException("unknown cluster privilege [" + name + "]. a privilege must be either " +
"one of the predefined cluster privilege names [" +
Strings.collectionToCommaDelimitedString(VALUES.keySet()) + "] or a pattern over one of the available " +
"cluster actions");
}
public static Set<String> names() {
return Collections.unmodifiableSet(VALUES.keySet());
}
public static boolean isClusterAction(String actionName) {
return actionName.startsWith("cluster:") || actionName.startsWith("indices:admin/template/");
}
private static String actionToPattern(String text) {
return text + "*";
}
}

View File

@ -10,18 +10,15 @@ import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.transport.TransportRequest;
import java.io.IOException;
import java.util.Collection;
import java.util.function.Predicate;
/**
* A ConditionalClusterPrivilege is a composition of a {@link ClusterPrivilege} (that determines which actions may be executed)
* with a {@link Predicate} for a {@link TransportRequest} (that determines which requests may be executed).
* The a given execution of an action is considered to be permitted if both the action and the request are permitted.
* A ConfigurableClusterPrivilege is a form of {@link ClusterPrivilege} that can be configured by an Elasticsearch security administrator
* within a {@link org.elasticsearch.xpack.core.security.authz.RoleDescriptor}.
*/
public interface ConditionalClusterPrivilege extends NamedWriteable, ToXContentFragment {
public interface ConfigurableClusterPrivilege extends NamedWriteable, ToXContentFragment, ClusterPrivilege {
/**
* The category under which this privilege should be rendered when output as XContent.
@ -29,17 +26,7 @@ public interface ConditionalClusterPrivilege extends NamedWriteable, ToXContentF
Category getCategory();
/**
* The action-level privilege that is required by this conditional privilege.
*/
ClusterPrivilege getPrivilege();
/**
* The request-level privilege (as a {@link Predicate}) that is required by this conditional privilege.
*/
Predicate<TransportRequest> getRequestPredicate();
/**
* A {@link ConditionalClusterPrivilege} should generate a fragment of {@code XContent}, which consists of
* A {@link ConfigurableClusterPrivilege} should generate a fragment of {@code XContent}, which consists of
* a single field name, followed by its value (which may be an object, an array, or a simple value).
*/
@Override
@ -47,8 +34,8 @@ public interface ConditionalClusterPrivilege extends NamedWriteable, ToXContentF
/**
* Categories exist for to segment privileges for the purposes of rendering to XContent.
* {@link ConditionalClusterPrivileges#toXContent(XContentBuilder, Params, Collection)} builds one XContent
* object for a collection of {@link ConditionalClusterPrivilege} instances, with the top level fields built
* {@link ConfigurableClusterPrivileges#toXContent(XContentBuilder, Params, Collection)} builds one XContent
* object for a collection of {@link ConfigurableClusterPrivilege} instances, with the top level fields built
* from the categories.
*/
enum Category {

View File

@ -17,7 +17,8 @@ import org.elasticsearch.common.xcontent.XContentParseException;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.action.privilege.ApplicationPrivilegesRequest;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege.Category;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege.Category;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.xcontent.XContentUtils;
@ -32,44 +33,44 @@ import java.util.Set;
import java.util.function.Predicate;
/**
* Static utility class for working with {@link ConditionalClusterPrivilege} instances
* Static utility class for working with {@link ConfigurableClusterPrivilege} instances
*/
public final class ConditionalClusterPrivileges {
public final class ConfigurableClusterPrivileges {
public static final ConditionalClusterPrivilege[] EMPTY_ARRAY = new ConditionalClusterPrivilege[0];
public static final ConfigurableClusterPrivilege[] EMPTY_ARRAY = new ConfigurableClusterPrivilege[0];
public static final Writeable.Reader<ConditionalClusterPrivilege> READER =
in1 -> in1.readNamedWriteable(ConditionalClusterPrivilege.class);
public static final Writeable.Writer<ConditionalClusterPrivilege> WRITER =
public static final Writeable.Reader<ConfigurableClusterPrivilege> READER =
in1 -> in1.readNamedWriteable(ConfigurableClusterPrivilege.class);
public static final Writeable.Writer<ConfigurableClusterPrivilege> WRITER =
(out1, value) -> out1.writeNamedWriteable(value);
private ConditionalClusterPrivileges() {
private ConfigurableClusterPrivileges() {
}
/**
* Utility method to read an array of {@link ConditionalClusterPrivilege} objects from a {@link StreamInput}
* Utility method to read an array of {@link ConfigurableClusterPrivilege} objects from a {@link StreamInput}
*/
public static ConditionalClusterPrivilege[] readArray(StreamInput in) throws IOException {
return in.readArray(READER, ConditionalClusterPrivilege[]::new);
public static ConfigurableClusterPrivilege[] readArray(StreamInput in) throws IOException {
return in.readArray(READER, ConfigurableClusterPrivilege[]::new);
}
/**
* Utility method to write an array of {@link ConditionalClusterPrivilege} objects to a {@link StreamOutput}
* Utility method to write an array of {@link ConfigurableClusterPrivilege} objects to a {@link StreamOutput}
*/
public static void writeArray(StreamOutput out, ConditionalClusterPrivilege[] privileges) throws IOException {
public static void writeArray(StreamOutput out, ConfigurableClusterPrivilege[] privileges) throws IOException {
out.writeArray(WRITER, privileges);
}
/**
* Writes a single object value to the {@code builder} that contains each of the provided privileges.
* The privileges are grouped according to their {@link ConditionalClusterPrivilege#getCategory() categories}
* The privileges are grouped according to their {@link ConfigurableClusterPrivilege#getCategory() categories}
*/
public static XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params,
Collection<ConditionalClusterPrivilege> privileges) throws IOException {
Collection<ConfigurableClusterPrivilege> privileges) throws IOException {
builder.startObject();
for (Category category : Category.values()) {
builder.startObject(category.field.getPreferredName());
for (ConditionalClusterPrivilege privilege : privileges) {
for (ConfigurableClusterPrivilege privilege : privileges) {
if (category == privilege.getCategory()) {
privilege.toXContent(builder, params);
}
@ -83,8 +84,8 @@ public final class ConditionalClusterPrivileges {
* Read a list of privileges from the parser. The parser should be positioned at the
* {@link XContentParser.Token#START_OBJECT} token for the privileges value
*/
public static List<ConditionalClusterPrivilege> parse(XContentParser parser) throws IOException {
List<ConditionalClusterPrivilege> privileges = new ArrayList<>();
public static List<ConfigurableClusterPrivilege> parse(XContentParser parser) throws IOException {
List<ConfigurableClusterPrivilege> privileges = new ArrayList<>();
expectedToken(parser.currentToken(), parser, XContentParser.Token.START_OBJECT);
while (parser.nextToken() != XContentParser.Token.END_OBJECT) {
@ -119,15 +120,13 @@ public final class ConditionalClusterPrivileges {
}
/**
* The {@code ManageApplicationPrivileges} privilege is a {@link ConditionalClusterPrivilege} that grants the
* The {@code ManageApplicationPrivileges} privilege is a {@link ConfigurableClusterPrivilege} that grants the
* ability to execute actions related to the management of application privileges (Get, Put, Delete) for a subset
* of applications (identified by a wildcard-aware application-name).
*/
public static class ManageApplicationPrivileges implements ConditionalClusterPrivilege {
public static class ManageApplicationPrivileges implements ConfigurableClusterPrivilege {
private static final ClusterPrivilege PRIVILEGE = ClusterPrivilege.get(
Collections.singleton("cluster:admin/xpack/security/privilege/*")
);
private static final Predicate<String> ACTION_PREDICATE = Automatons.predicate("cluster:admin/xpack/security/privilege/*");
public static final String WRITEABLE_NAME = "manage-application-privileges";
private final Set<String> applicationNames;
@ -153,16 +152,6 @@ public final class ConditionalClusterPrivileges {
return Category.APPLICATION;
}
@Override
public ClusterPrivilege getPrivilege() {
return PRIVILEGE;
}
@Override
public Predicate<TransportRequest> getRequestPredicate() {
return this.requestPredicate;
}
public Collection<String> getApplicationNames() {
return Collections.unmodifiableCollection(this.applicationNames);
}
@ -224,6 +213,11 @@ public final class ConditionalClusterPrivileges {
return applicationNames.hashCode();
}
@Override
public ClusterPermission.Builder buildPermission(final ClusterPermission.Builder builder) {
return builder.add(this, ACTION_PREDICATE, requestPredicate);
}
private interface Fields {
ParseField MANAGE = new ParseField("manage");
ParseField APPLICATIONS = new ParseField("applications");

View File

@ -0,0 +1,19 @@
/*
*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*
*/
package org.elasticsearch.xpack.core.security.authz.privilege;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
/**
* A {@link ClusterPrivilege} that has a name. The named cluster privileges can be referred simply by name within a
* {@link RoleDescriptor#getClusterPrivileges()}.
*/
public interface NamedClusterPrivilege extends ClusterPrivilege {
String name();
}

View File

@ -12,8 +12,8 @@ import org.elasticsearch.xpack.core.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
import org.elasticsearch.xpack.core.security.user.UsernamesField;
@ -125,7 +125,7 @@ public class ReservedRolesStore implements BiConsumer<Set<String>, ActionListene
.indices(".code-*", ".code_internal-*").privileges("all").build(),
},
null,
new ConditionalClusterPrivilege[] { new ManageApplicationPrivileges(Collections.singleton("kibana-*")) },
new ConfigurableClusterPrivilege[] { new ManageApplicationPrivileges(Collections.singleton("kibana-*")) },
null, MetadataUtils.DEFAULT_RESERVED_METADATA, null))
.put("logstash_system", new RoleDescriptor("logstash_system", new String[] { "monitor", MonitoringBulkAction.NAME},
null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))

View File

@ -22,7 +22,7 @@ import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.xpack.core.XPackClientPlugin;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import java.io.IOException;
import java.util.Arrays;
@ -189,7 +189,7 @@ public class PutRoleRequestTests extends ESTestCase {
if (randomBoolean()) {
final String[] appNames = randomArray(1, 4, String[]::new, stringWithInitialLowercase);
request.conditionalCluster(new ConditionalClusterPrivileges.ManageApplicationPrivileges(Sets.newHashSet(appNames)));
request.conditionalCluster(new ConfigurableClusterPrivileges.ManageApplicationPrivileges(Sets.newHashSet(appNames)));
}
request.runAs(generateRandomStringArray(4, 3, false, true));

View File

@ -21,8 +21,8 @@ import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.xpack.core.XPackClientPlugin;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition.FieldGrantExcludeGroup;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges.ManageApplicationPrivileges;
import java.io.IOException;
import java.util.ArrayList;
@ -75,7 +75,7 @@ public class GetUserPrivilegesResponseTests extends ESTestCase {
public GetUserPrivilegesResponse mutate(GetUserPrivilegesResponse original) {
final int random = randomIntBetween(1, 0b11111);
final Set<String> cluster = maybeMutate(random, 0, original.getClusterPrivileges(), () -> randomAlphaOfLength(5));
final Set<ConditionalClusterPrivilege> conditionalCluster = maybeMutate(random, 1,
final Set<ConfigurableClusterPrivilege> conditionalCluster = maybeMutate(random, 1,
original.getConditionalClusterPrivileges(), () -> new ManageApplicationPrivileges(randomStringSet(3)));
final Set<GetUserPrivilegesResponse.Indices> index = maybeMutate(random, 2, original.getIndexPrivileges(),
() -> new GetUserPrivilegesResponse.Indices(randomStringSet(1), randomStringSet(1), emptySet(), emptySet(),
@ -103,7 +103,7 @@ public class GetUserPrivilegesResponseTests extends ESTestCase {
private GetUserPrivilegesResponse randomResponse() {
final Set<String> cluster = randomStringSet(5);
final Set<ConditionalClusterPrivilege> conditionalCluster = Sets.newHashSet(randomArray(3, ConditionalClusterPrivilege[]::new,
final Set<ConfigurableClusterPrivilege> conditionalCluster = Sets.newHashSet(randomArray(3, ConfigurableClusterPrivilege[]::new,
() -> new ManageApplicationPrivileges(
randomStringSet(3)
)));

View File

@ -15,7 +15,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import java.io.IOException;
import java.util.Arrays;
@ -98,9 +98,11 @@ public class HasPrivilegesRequestTests extends ESTestCase {
final HasPrivilegesRequest request = new HasPrivilegesRequest();
request.username(randomAlphaOfLength(8));
final List<String> clusterPrivileges = randomSubsetOf(Arrays.asList(ClusterPrivilege.MONITOR, ClusterPrivilege.MANAGE,
ClusterPrivilege.MANAGE_ML, ClusterPrivilege.MANAGE_SECURITY, ClusterPrivilege.MANAGE_PIPELINE, ClusterPrivilege.ALL))
.stream().flatMap(p -> p.name().stream()).collect(Collectors.toList());
final List<String> clusterPrivileges = randomSubsetOf(Arrays.asList(ClusterPrivilegeResolver.MONITOR,
ClusterPrivilegeResolver.MANAGE,
ClusterPrivilegeResolver.MANAGE_ML, ClusterPrivilegeResolver.MANAGE_SECURITY, ClusterPrivilegeResolver.MANAGE_PIPELINE,
ClusterPrivilegeResolver.ALL))
.stream().map(p -> p.name()).collect(Collectors.toList());
request.clusterPrivileges(clusterPrivileges.toArray(Strings.EMPTY_ARRAY));
IndicesPrivileges[] indicesPrivileges = new IndicesPrivileges[randomInt(5)];

View File

@ -0,0 +1,282 @@
/*
*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*
*/
package org.elasticsearch.xpack.core.security.authz.permission;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.junit.Before;
import org.mockito.Mockito;
import java.io.IOException;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
public class ClusterPermissionTests extends ESTestCase {
private TransportRequest mockTransportRequest = Mockito.mock(TransportRequest.class);
private ClusterPrivilege cpThatDoesNothing = new ClusterPrivilege() {
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
return builder;
}
};
@Before
public void setup() {
mockTransportRequest = Mockito.mock(TransportRequest.class);
}
public void testClusterPermissionBuilder() {
ClusterPermission.Builder builder = ClusterPermission.builder();
assertNotNull(builder);
assertThat(builder.build(), is(ClusterPermission.NONE));
builder = ClusterPrivilegeResolver.MANAGE_SECURITY.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege2 =
new MockConfigurableClusterPrivilege(r -> false);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
builder = mockConfigurableClusterPrivilege2.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
assertNotNull(clusterPermission);
assertNotNull(clusterPermission.privileges());
final Set<ClusterPrivilege> privileges = clusterPermission.privileges();
assertNotNull(privileges);
assertThat(privileges.size(), is(4));
assertThat(privileges, containsInAnyOrder(ClusterPrivilegeResolver.MANAGE_SECURITY, ClusterPrivilegeResolver.MANAGE_ILM,
mockConfigurableClusterPrivilege1, mockConfigurableClusterPrivilege2));
}
public void testClusterPermissionCheck() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_SECURITY.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege2 =
new MockConfigurableClusterPrivilege(r -> false);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
builder = mockConfigurableClusterPrivilege2.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.check("cluster:admin/xpack/security/token/invalidate", mockTransportRequest), is(true));
assertThat(clusterPermission.check("cluster:admin/ilm/stop", mockTransportRequest), is(true));
assertThat(clusterPermission.check("cluster:admin/xpack/security/privilege/get", mockTransportRequest), is(true));
assertThat(clusterPermission.check("cluster:admin/snapshot/status", mockTransportRequest), is(false));
}
public void testClusterPermissionCheckWithEmptyActionPatterns() {
final ClusterPermission.Builder builder = ClusterPermission.builder();
builder.add(cpThatDoesNothing, Collections.emptySet(), Collections.emptySet());
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.check("cluster:admin/ilm/start", mockTransportRequest), is(false));
assertThat(clusterPermission.check("cluster:admin/xpack/security/token/invalidate", mockTransportRequest), is(false));
}
public void testClusterPermissionCheckWithExcludeOnlyActionPatterns() {
final ClusterPermission.Builder builder = ClusterPermission.builder();
builder.add(cpThatDoesNothing, Collections.emptySet(), Collections.singleton("cluster:some/thing/to/exclude"));
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.check("cluster:admin/ilm/start", mockTransportRequest), is(false));
assertThat(clusterPermission.check("cluster:admin/xpack/security/token/invalidate", mockTransportRequest), is(false));
}
public void testClusterPermissionCheckWithActionPatterns() {
final ClusterPermission.Builder builder = ClusterPermission.builder();
builder.add(cpThatDoesNothing, Collections.singleton("cluster:admin/*"), Collections.singleton("cluster:admin/ilm/*"));
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.check("cluster:admin/ilm/start", mockTransportRequest), is(false));
assertThat(clusterPermission.check("cluster:admin/xpack/security/token/invalidate", mockTransportRequest), is(true));
}
public void testClusterPermissionCheckWithActionPatternsAndNoExludePatterns() {
final ClusterPermission.Builder builder = ClusterPermission.builder();
builder.add(cpThatDoesNothing, Collections.singleton("cluster:admin/*"), Collections.emptySet());
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.check("cluster:admin/ilm/start", mockTransportRequest), is(true));
assertThat(clusterPermission.check("cluster:admin/xpack/security/token/invalidate", mockTransportRequest), is(true));
}
public void testNoneClusterPermissionIsImpliedByNone() {
assertThat(ClusterPermission.NONE.implies(ClusterPermission.NONE), is(true));
}
public void testNoneClusterPermissionIsImpliedByAny() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_SECURITY.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege2 =
new MockConfigurableClusterPrivilege(r -> false);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
builder = mockConfigurableClusterPrivilege2.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.implies(ClusterPermission.NONE), is(true));
}
public void testClusterPermissionSubsetWithConfigurableClusterPrivilegeIsImpliedByClusterPermission() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
ClusterPermission.Builder builder1 = ClusterPermission.builder();
builder1 = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder1);
builder1 = mockConfigurableClusterPrivilege1.buildPermission(builder1);
final ClusterPermission otherClusterPermission = builder1.build();
assertThat(clusterPermission.implies(otherClusterPermission), is(true));
}
public void testClusterPermissionNonSubsetWithConfigurableClusterPrivilegeIsImpliedByClusterPermission() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
ClusterPermission.Builder builder1 = ClusterPermission.builder();
builder1 = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder1);
builder1 = mockConfigurableClusterPrivilege1.buildPermission(builder1);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege2 =
new MockConfigurableClusterPrivilege(r -> false);
builder1 = mockConfigurableClusterPrivilege2.buildPermission(builder1);
final ClusterPermission otherClusterPermission = builder1.build();
assertThat(clusterPermission.implies(otherClusterPermission), is(false));
}
public void testClusterPermissionNonSubsetIsNotImpliedByClusterPermission() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
ClusterPermission.Builder builder1 = ClusterPermission.builder();
builder1 = ClusterPrivilegeResolver.MANAGE_API_KEY.buildPermission(builder1);
final ClusterPermission otherClusterPermission = builder1.build();
assertThat(clusterPermission.implies(otherClusterPermission), is(false));
}
public void testClusterPermissionSubsetIsImpliedByClusterPermission() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
ClusterPermission.Builder builder1 = ClusterPermission.builder();
builder1 = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder1);
final ClusterPermission otherClusterPermission = builder1.build();
assertThat(clusterPermission.implies(otherClusterPermission), is(true));
}
public void testClusterPermissionIsImpliedBySameClusterPermission() {
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = ClusterPrivilegeResolver.MANAGE_ML.buildPermission(builder);
builder = ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(builder);
final MockConfigurableClusterPrivilege mockConfigurableClusterPrivilege1 =
new MockConfigurableClusterPrivilege(r -> r == mockTransportRequest);
builder = mockConfigurableClusterPrivilege1.buildPermission(builder);
final ClusterPermission clusterPermission = builder.build();
assertThat(clusterPermission.implies(clusterPermission), is(true));
}
public void testClusterPermissionSubsetIsImpliedByAllClusterPermission() {
final ClusterPermission allClusterPermission = ClusterPrivilegeResolver.ALL.buildPermission(ClusterPermission.builder()).build();
ClusterPermission otherClusterPermission =
ClusterPrivilegeResolver.MANAGE_ILM.buildPermission(ClusterPermission.builder()).build();
assertThat(allClusterPermission.implies(otherClusterPermission), is(true));
}
private static class MockConfigurableClusterPrivilege implements ConfigurableClusterPrivilege {
static final Predicate<String> ACTION_PREDICATE = Automatons.predicate("cluster:admin/xpack/security/privilege/*");
private Predicate<TransportRequest> requestPredicate;
MockConfigurableClusterPrivilege(Predicate<TransportRequest> requestPredicate) {
this.requestPredicate = requestPredicate;
}
@Override
public Category getCategory() {
return Category.APPLICATION;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder;
}
@Override
public String getWriteableName() {
return "mock-ccp";
}
@Override
public void writeTo(StreamOutput out) throws IOException {
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final MockConfigurableClusterPrivilege that = (MockConfigurableClusterPrivilege) o;
return requestPredicate.equals(that.requestPredicate);
}
@Override
public int hashCode() {
return Objects.hash(requestPredicate);
}
@Override
public String toString() {
return "MockConfigurableClusterPrivilege{" +
"requestPredicate=" + requestPredicate +
'}';
}
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
return builder.add(this, ACTION_PREDICATE, requestPredicate);
}
}
}

View File

@ -21,7 +21,7 @@ import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.junit.Before;
@ -200,27 +200,27 @@ public class LimitedRoleTests extends ESTestCase {
public void testCheckClusterPrivilege() {
Role fromRole = Role.builder("a-role").cluster(Collections.singleton("manage_security"), Collections.emptyList())
.build();
assertThat(fromRole.grants(ClusterPrivilege.ALL), is(false));
assertThat(fromRole.grants(ClusterPrivilege.MANAGE_SECURITY), is(true));
assertThat(fromRole.grants(ClusterPrivilegeResolver.ALL), is(false));
assertThat(fromRole.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true));
{
Role limitedByRole = Role.builder("scoped-role")
.cluster(Collections.singleton("all"), Collections.emptyList()).build();
assertThat(limitedByRole.grants(ClusterPrivilege.ALL), is(true));
assertThat(limitedByRole.grants(ClusterPrivilege.MANAGE_SECURITY), is(true));
assertThat(limitedByRole.grants(ClusterPrivilegeResolver.ALL), is(true));
assertThat(limitedByRole.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true));
Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole);
assertThat(role.grants(ClusterPrivilege.ALL), is(false));
assertThat(role.grants(ClusterPrivilege.MANAGE_SECURITY), is(true));
assertThat(role.grants(ClusterPrivilegeResolver.ALL), is(false));
assertThat(role.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(true));
}
{
Role limitedByRole = Role.builder("scoped-role")
.cluster(Collections.singleton("monitor"), Collections.emptyList()).build();
assertThat(limitedByRole.grants(ClusterPrivilege.ALL), is(false));
assertThat(limitedByRole.grants(ClusterPrivilege.MONITOR), is(true));
assertThat(limitedByRole.grants(ClusterPrivilegeResolver.ALL), is(false));
assertThat(limitedByRole.grants(ClusterPrivilegeResolver.MONITOR), is(true));
Role role = LimitedRole.createLimitedRole(fromRole, limitedByRole);
assertThat(role.grants(ClusterPrivilege.ALL), is(false));
assertThat(role.grants(ClusterPrivilege.MANAGE_SECURITY), is(false));
assertThat(role.grants(ClusterPrivilege.MONITOR), is(false));
assertThat(role.grants(ClusterPrivilegeResolver.ALL), is(false));
assertThat(role.grants(ClusterPrivilegeResolver.MANAGE_SECURITY), is(false));
assertThat(role.grants(ClusterPrivilegeResolver.MONITOR), is(false));
}
}

View File

@ -27,15 +27,15 @@ import java.util.List;
import static org.elasticsearch.common.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION;
import static org.hamcrest.Matchers.equalTo;
public class ConditionalClusterPrivilegesTests extends ESTestCase {
public class ConfigurableClusterPrivilegesTests extends ESTestCase {
public void testSerialization() throws Exception {
final ConditionalClusterPrivilege[] original = buildSecurityPrivileges();
final ConfigurableClusterPrivilege[] original = buildSecurityPrivileges();
try (BytesStreamOutput out = new BytesStreamOutput()) {
ConditionalClusterPrivileges.writeArray(out, original);
ConfigurableClusterPrivileges.writeArray(out, original);
final NamedWriteableRegistry registry = new NamedWriteableRegistry(new XPackClientPlugin(Settings.EMPTY).getNamedWriteables());
try (StreamInput in = new NamedWriteableAwareStreamInput(out.bytes().streamInput(), registry)) {
final ConditionalClusterPrivilege[] copy = ConditionalClusterPrivileges.readArray(in);
final ConfigurableClusterPrivilege[] copy = ConfigurableClusterPrivileges.readArray(in);
assertThat(copy, equalTo(original));
assertThat(original, equalTo(copy));
}
@ -47,26 +47,26 @@ public class ConditionalClusterPrivilegesTests extends ESTestCase {
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
final XContentBuilder builder = new XContentBuilder(xContent, out);
final List<ConditionalClusterPrivilege> original = Arrays.asList(buildSecurityPrivileges());
ConditionalClusterPrivileges.toXContent(builder, ToXContent.EMPTY_PARAMS, original);
final List<ConfigurableClusterPrivilege> original = Arrays.asList(buildSecurityPrivileges());
ConfigurableClusterPrivileges.toXContent(builder, ToXContent.EMPTY_PARAMS, original);
builder.flush();
final byte[] bytes = out.toByteArray();
try (XContentParser parser = xContent.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, bytes)) {
assertThat(parser.nextToken(), equalTo(XContentParser.Token.START_OBJECT));
final List<ConditionalClusterPrivilege> clone = ConditionalClusterPrivileges.parse(parser);
final List<ConfigurableClusterPrivilege> clone = ConfigurableClusterPrivileges.parse(parser);
assertThat(clone, equalTo(original));
assertThat(original, equalTo(clone));
}
}
}
private ConditionalClusterPrivilege[] buildSecurityPrivileges() {
private ConfigurableClusterPrivilege[] buildSecurityPrivileges() {
return buildSecurityPrivileges(randomIntBetween(4, 7));
}
private ConditionalClusterPrivilege[] buildSecurityPrivileges(int applicationNameLength) {
return new ConditionalClusterPrivilege[] {
private ConfigurableClusterPrivilege[] buildSecurityPrivileges(int applicationNameLength) {
return new ConfigurableClusterPrivilege[] {
ManageApplicationPrivilegesTests.buildPrivileges(applicationNameLength)
};
}

View File

@ -20,19 +20,12 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.XPackClientPlugin;
import org.elasticsearch.xpack.core.security.action.privilege.DeletePrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.DeletePrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.privilege.GetPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetPrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.privilege.PutPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.PutPrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.role.PutRoleAction;
import org.elasticsearch.xpack.core.security.action.rolemapping.DeleteRoleMappingAction;
import org.elasticsearch.xpack.core.security.action.user.GetUsersAction;
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesAction;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges.ManageApplicationPrivileges;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
@ -42,13 +35,9 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Predicate;
import static org.elasticsearch.common.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION;
import static org.elasticsearch.test.TestMatchers.predicateMatches;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
@ -100,34 +89,22 @@ public class ManageApplicationPrivilegesTests extends ESTestCase {
EqualsHashCodeTestUtils.checkEqualsAndHashCode(privileges, this::clone, mutate);
}
public void testPrivilege() {
final ManageApplicationPrivileges privileges = buildPrivileges();
assertThat(privileges.getPrivilege(), instanceOf(ClusterPrivilege.class));
for (String actionName : Arrays.asList(GetPrivilegesAction.NAME, PutPrivilegesAction.NAME, DeletePrivilegesAction.NAME)) {
assertThat(privileges.getPrivilege().predicate(), predicateMatches(actionName));
}
for (String actionName : Arrays.asList(GetUsersAction.NAME, PutRoleAction.NAME, DeleteRoleMappingAction.NAME,
HasPrivilegesAction.NAME)) {
assertThat(privileges.getPrivilege().predicate(), not(predicateMatches(actionName)));
}
}
public void testRequestPredicate() {
public void testActionAndRequestPredicate() {
final ManageApplicationPrivileges kibanaAndLogstash = new ManageApplicationPrivileges(Sets.newHashSet("kibana-*", "logstash"));
final ManageApplicationPrivileges cloudAndSwiftype = new ManageApplicationPrivileges(Sets.newHashSet("cloud-*", "swiftype"));
final Predicate<TransportRequest> kibanaAndLogstashPredicate = kibanaAndLogstash.getRequestPredicate();
final Predicate<TransportRequest> cloudAndSwiftypePredicate = cloudAndSwiftype.getRequestPredicate();
assertThat(kibanaAndLogstashPredicate, notNullValue());
assertThat(cloudAndSwiftypePredicate, notNullValue());
final ClusterPermission kibanaAndLogstashPermission = kibanaAndLogstash.buildPermission(ClusterPermission.builder()).build();
final ClusterPermission cloudAndSwiftypePermission = cloudAndSwiftype.buildPermission(ClusterPermission.builder()).build();
assertThat(kibanaAndLogstashPermission, notNullValue());
assertThat(cloudAndSwiftypePermission, notNullValue());
final GetPrivilegesRequest getKibana1 = new GetPrivilegesRequest();
getKibana1.application("kibana-1");
assertThat(kibanaAndLogstashPredicate, predicateMatches(getKibana1));
assertThat(cloudAndSwiftypePredicate, not(predicateMatches(getKibana1)));
assertTrue(kibanaAndLogstashPermission.check("cluster:admin/xpack/security/privilege/get", getKibana1));
assertFalse(cloudAndSwiftypePermission.check("cluster:admin/xpack/security/privilege/get", getKibana1));
final DeletePrivilegesRequest deleteLogstash = new DeletePrivilegesRequest("logstash", new String[]{"all"});
assertThat(kibanaAndLogstashPredicate, predicateMatches(deleteLogstash));
assertThat(cloudAndSwiftypePredicate, not(predicateMatches(deleteLogstash)));
assertTrue(kibanaAndLogstashPermission.check("cluster:admin/xpack/security/privilege/get", deleteLogstash));
assertFalse(cloudAndSwiftypePermission.check("cluster:admin/xpack/security/privilege/get", deleteLogstash));
final PutPrivilegesRequest putKibana = new PutPrivilegesRequest();
@ -137,8 +114,8 @@ public class ManageApplicationPrivilegesTests extends ESTestCase {
randomAlphaOfLengthBetween(3, 6).toLowerCase(Locale.ROOT), Collections.emptySet(), Collections.emptyMap()));
}
putKibana.setPrivileges(kibanaPrivileges);
assertThat(kibanaAndLogstashPredicate, predicateMatches(putKibana));
assertThat(cloudAndSwiftypePredicate, not(predicateMatches(putKibana)));
assertTrue(kibanaAndLogstashPermission.check("cluster:admin/xpack/security/privilege/get", putKibana));
assertFalse(cloudAndSwiftypePermission.check("cluster:admin/xpack/security/privilege/get", putKibana));
}
public void testSecurityForGetAllApplicationPrivileges() {
@ -151,8 +128,10 @@ public class ManageApplicationPrivilegesTests extends ESTestCase {
final ManageApplicationPrivileges kibanaOnly = new ManageApplicationPrivileges(Sets.newHashSet("kibana-*"));
final ManageApplicationPrivileges allApps = new ManageApplicationPrivileges(Sets.newHashSet("*"));
assertThat(kibanaOnly.getRequestPredicate(), not(predicateMatches(getAll)));
assertThat(allApps.getRequestPredicate(), predicateMatches(getAll));
final ClusterPermission kibanaOnlyPermission = kibanaOnly.buildPermission(ClusterPermission.builder()).build();
final ClusterPermission allAppsPermission = allApps.buildPermission(ClusterPermission.builder()).build();
assertFalse(kibanaOnlyPermission.check("cluster:admin/xpack/security/privilege/get", getAll));
assertTrue(allAppsPermission.check("cluster:admin/xpack/security/privilege/get", getAll));
}
private ManageApplicationPrivileges clone(ManageApplicationPrivileges original) {

View File

@ -8,9 +8,12 @@ package org.elasticsearch.xpack.core.security.authz.privilege;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import java.util.Set;
import java.util.function.Predicate;
@ -29,55 +32,78 @@ public class PrivilegeTests extends ESTestCase {
assertThat(predicate.test("bar[n][nodes]"), is(false));
assertThat(predicate.test("[n][nodes]"), is(false));
}
private void verifyClusterActionAllowed(ClusterPrivilege clusterPrivilege, String... actions) {
ClusterPermission clusterPermission = clusterPrivilege.buildPermission(ClusterPermission.builder()).build();
for (String action: actions) {
assertTrue(clusterPermission.check(action, Mockito.mock(TransportRequest.class)));
}
}
private void verifyClusterActionDenied(ClusterPrivilege clusterPrivilege, String... actions) {
ClusterPermission clusterPermission = clusterPrivilege.buildPermission(ClusterPermission.builder()).build();
for (String action: actions) {
assertFalse(clusterPermission.check(action, Mockito.mock(TransportRequest.class)));
}
}
public void testCluster() throws Exception {
Set<String> name = Sets.newHashSet("monitor");
ClusterPrivilege cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.MONITOR));
ClusterPrivilege allClusterPrivilege = ClusterPrivilegeResolver.resolve("all");
assertThat(allClusterPrivilege, is(ClusterPrivilegeResolver.ALL));
verifyClusterActionAllowed(allClusterPrivilege, "cluster:admin/xpack/security/*");
// since "all" implies "monitor", this should be the same language as All
name = Sets.newHashSet("monitor", "all");
cluster = ClusterPrivilege.get(name);
assertTrue(Operations.sameLanguage(ClusterPrivilege.ALL.automaton, cluster.automaton));
ClusterPrivilege monitorClusterPrivilege = ClusterPrivilegeResolver.resolve("monitor");
assertThat(monitorClusterPrivilege, is(ClusterPrivilegeResolver.MONITOR));
verifyClusterActionAllowed(monitorClusterPrivilege, "cluster:monitor/*");
verifyClusterActionDenied(monitorClusterPrivilege, "cluster:admin/xpack/security/*");
name = Sets.newHashSet("monitor", "none");
cluster = ClusterPrivilege.get(name);
assertTrue(Operations.sameLanguage(ClusterPrivilege.MONITOR.automaton, cluster.automaton));
ClusterPrivilege noneClusterPrivilege = ClusterPrivilegeResolver.resolve("none");
assertThat(noneClusterPrivilege, is(ClusterPrivilegeResolver.NONE));
verifyClusterActionDenied(noneClusterPrivilege, "cluster:admin/xpack/security/*");
verifyClusterActionDenied(noneClusterPrivilege, "cluster:monitor/*");
verifyClusterActionDenied(noneClusterPrivilege, "*");
Set<String> name2 = Sets.newHashSet("none", "monitor");
ClusterPrivilege cluster2 = ClusterPrivilege.get(name2);
assertThat(cluster, is(cluster2));
ClusterPermission monitorClusterPermission = monitorClusterPrivilege.buildPermission(ClusterPermission.builder()).build();
ClusterPermission allClusterPermission = allClusterPrivilege.buildPermission(ClusterPermission.builder()).build();
// all implies monitor
assertTrue(allClusterPermission.implies(monitorClusterPermission));
ClusterPermission.Builder builder = ClusterPermission.builder();
builder = allClusterPrivilege.buildPermission(builder);
builder = noneClusterPrivilege.buildPermission(builder);
ClusterPermission combinedPermission = builder.build();
assertTrue(combinedPermission.implies(monitorClusterPermission));
}
public void testClusterTemplateActions() throws Exception {
Set<String> name = Sets.newHashSet("indices:admin/template/delete");
ClusterPrivilege cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/delete"), is(true));
ClusterPrivilege clusterPrivilegeTemplateDelete = ClusterPrivilegeResolver.resolve("indices:admin/template/delete");
assertThat(clusterPrivilegeTemplateDelete, notNullValue());
verifyClusterActionAllowed(clusterPrivilegeTemplateDelete, "indices:admin/template/delete");
verifyClusterActionDenied(clusterPrivilegeTemplateDelete, "indices:admin/template/get", "indices:admin/template/put");
name = Sets.newHashSet("indices:admin/template/get");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/get"), is(true));
ClusterPrivilege clusterPrivilegeTemplateGet = ClusterPrivilegeResolver.resolve("indices:admin/template/get");
assertThat(clusterPrivilegeTemplateGet, notNullValue());
verifyClusterActionAllowed(clusterPrivilegeTemplateGet, "indices:admin/template/get");
verifyClusterActionDenied(clusterPrivilegeTemplateGet, "indices:admin/template/delete", "indices:admin/template/put");
name = Sets.newHashSet("indices:admin/template/put");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/put"), is(true));
ClusterPrivilege clusterPrivilegeTemplatePut = ClusterPrivilegeResolver.resolve("indices:admin/template/put");
assertThat(clusterPrivilegeTemplatePut, notNullValue());
verifyClusterActionAllowed(clusterPrivilegeTemplatePut, "indices:admin/template/put");
verifyClusterActionDenied(clusterPrivilegeTemplatePut, "indices:admin/template/get", "indices:admin/template/delete");
}
public void testClusterInvalidName() throws Exception {
thrown.expect(IllegalArgumentException.class);
Set<String> actionName = Sets.newHashSet("foobar");
ClusterPrivilege.get(actionName);
ClusterPrivilegeResolver.resolve("foobar");
}
public void testClusterAction() throws Exception {
Set<String> actionName = Sets.newHashSet("cluster:admin/snapshot/delete");
ClusterPrivilege cluster = ClusterPrivilege.get(actionName);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("cluster:admin/snapshot/delete"), is(true));
assertThat(cluster.predicate().test("cluster:admin/snapshot/dele"), is(false));
// ClusterPrivilegeResolver.resolve() for a cluster action converts action name into a pattern by adding "*"
ClusterPrivilege clusterPrivilegeSnapshotDelete = ClusterPrivilegeResolver.resolve("cluster:admin/snapshot/delete");
assertThat(clusterPrivilegeSnapshotDelete, notNullValue());
verifyClusterActionAllowed(clusterPrivilegeSnapshotDelete, "cluster:admin/snapshot/delete", "cluster:admin/snapshot/delete[n]",
"cluster:admin/snapshot/delete/non-existing");
verifyClusterActionDenied(clusterPrivilegeSnapshotDelete, "cluster:admin/snapshot/dele", "cluster:admin/snapshot/dele[n]",
"cluster:admin/snapshot/dele/non-existing");
}
public void testIndexAction() throws Exception {
@ -144,42 +170,28 @@ public class PrivilegeTests extends ESTestCase {
}
public void testManageCcrPrivilege() {
Predicate<String> predicate = ClusterPrivilege.MANAGE_CCR.predicate();
assertThat(predicate.test("cluster:admin/xpack/ccr/follow_index"), is(true));
assertThat(predicate.test("cluster:admin/xpack/ccr/unfollow_index"), is(true));
assertThat(predicate.test("cluster:admin/xpack/ccr/brand_new_api"), is(true));
assertThat(predicate.test("cluster:admin/xpack/whatever"), is(false));
verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_CCR, "cluster:admin/xpack/ccr/follow_index",
"cluster:admin/xpack/ccr/unfollow_index", "cluster:admin/xpack/ccr/brand_new_api");
verifyClusterActionDenied(ClusterPrivilegeResolver.MANAGE_CCR, "cluster:admin/xpack/whatever");
}
public void testIlmPrivileges() {
{
Predicate<String> predicate = ClusterPrivilege.MANAGE_ILM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/ilm/delete"), is(true));
assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(true));
assertThat(predicate.test("cluster:admin/ilm/put"), is(true));
assertThat(predicate.test("cluster:admin/ilm/start"), is(true));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(true));
assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(true));
assertThat(predicate.test("cluster:admin/ilm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-ilm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_ILM, "cluster:admin/ilm/delete",
"cluster:admin/ilm/_move/post", "cluster:admin/ilm/put", "cluster:admin/ilm/start",
"cluster:admin/ilm/stop", "cluster:admin/ilm/brand_new_api", "cluster:admin/ilm/get",
"cluster:admin/ilm/operation_mode/get"
);
verifyClusterActionDenied(ClusterPrivilegeResolver.MANAGE_ILM, "cluster:admin/whatever");
}
{
Predicate<String> predicate = ClusterPrivilege.READ_ILM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/ilm/delete"), is(false));
assertThat(predicate.test("cluster:admin/ilm/_move/post"), is(false));
assertThat(predicate.test("cluster:admin/ilm/put"), is(false));
assertThat(predicate.test("cluster:admin/ilm/start"), is(false));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(false));
assertThat(predicate.test("cluster:admin/ilm/brand_new_api"), is(false));
assertThat(predicate.test("cluster:admin/ilm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-ilm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
verifyClusterActionAllowed(ClusterPrivilegeResolver.READ_ILM, "cluster:admin/ilm/get", "cluster:admin/ilm/operation_mode/get");
verifyClusterActionDenied(ClusterPrivilegeResolver.READ_ILM, "cluster:admin/ilm/delete", "cluster:admin/ilm/_move/post",
"cluster:admin/ilm/put", "cluster:admin/ilm/start", "cluster:admin/ilm/stop",
"cluster:admin/ilm/brand_new_api", "cluster:admin/whatever");
}
{
@ -205,33 +217,29 @@ public class PrivilegeTests extends ESTestCase {
}
}
public void testSlmPriviledges() {
public void testSlmPrivileges() {
{
Predicate<String> predicate = ClusterPrivilege.MANAGE_SLM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/slm/delete"), is(true));
assertThat(predicate.test("cluster:admin/slm/put"), is(true));
assertThat(predicate.test("cluster:admin/slm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/start"), is(true));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(true));
assertThat(predicate.test("cluster:admin/slm/execute"), is(true));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-slm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
verifyClusterActionAllowed(ClusterPrivilegeResolver.MANAGE_SLM, "cluster:admin/slm/delete",
"cluster:admin/slm/put",
"cluster:admin/slm/get",
"cluster:admin/ilm/start",
"cluster:admin/ilm/stop",
"cluster:admin/slm/execute",
"cluster:admin/ilm/operation_mode/get");
verifyClusterActionDenied(ClusterPrivilegeResolver.MANAGE_SLM, "cluster:admin/whatever");
}
{
Predicate<String> predicate = ClusterPrivilege.READ_SLM.predicate();
// check cluster actions
assertThat(predicate.test("cluster:admin/slm/delete"), is(false));
assertThat(predicate.test("cluster:admin/slm/put"), is(false));
assertThat(predicate.test("cluster:admin/slm/get"), is(true));
assertThat(predicate.test("cluster:admin/ilm/start"), is(false));
assertThat(predicate.test("cluster:admin/ilm/stop"), is(false));
assertThat(predicate.test("cluster:admin/slm/execute"), is(false));
assertThat(predicate.test("cluster:admin/ilm/operation_mode/get"), is(true));
// check non-slm action
assertThat(predicate.test("cluster:admin/whatever"), is(false));
verifyClusterActionAllowed(ClusterPrivilegeResolver.READ_SLM,
"cluster:admin/slm/get",
"cluster:admin/ilm/operation_mode/get");
verifyClusterActionDenied(ClusterPrivilegeResolver.READ_SLM,"cluster:admin/slm/delete",
"cluster:admin/slm/put",
"cluster:admin/ilm/start",
"cluster:admin/ilm/stop",
"cluster:admin/slm/execute",
"cluster:admin/whatever");
}
}
}

View File

@ -14,7 +14,7 @@ import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.privilege.GetBuiltinPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import java.util.TreeSet;
@ -31,7 +31,7 @@ public class TransportGetBuiltinPrivilegesAction extends HandledTransportAction<
@Override
protected void doExecute(Task task, GetBuiltinPrivilegesRequest request, ActionListener<GetBuiltinPrivilegesResponse> listener) {
final TreeSet<String> cluster = new TreeSet<>(ClusterPrivilege.names());
final TreeSet<String> cluster = new TreeSet<>(ClusterPrivilegeResolver.names());
final TreeSet<String> index = new TreeSet<>(IndexPrivilege.names());
listener.onResponse(new GetBuiltinPrivilegesResponse(cluster, index));
}

View File

@ -54,7 +54,7 @@ import org.elasticsearch.xpack.core.security.authz.AuthorizationServiceField;
import org.elasticsearch.xpack.core.security.authz.ResolvedIndices;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
@ -231,7 +231,7 @@ public class AuthorizationService {
final TransportRequest request = requestInfo.getRequest();
final String action = requestInfo.getAction();
final AuthorizationEngine authzEngine = getAuthorizationEngine(authentication);
if (ClusterPrivilege.ACTION_MATCHER.test(action)) {
if (ClusterPrivilegeResolver.isClusterAction(action)) {
final ActionListener<AuthorizationResult> clusterAuthzListener =
wrapPreservingContext(new AuthorizationResultListener<>(result -> {
putTransientIfNonExisting(AuthorizationServiceField.INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);

View File

@ -28,7 +28,6 @@ import org.elasticsearch.action.termvectors.MultiTermVectorsAction;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.transport.TransportActionProxy;
@ -57,7 +56,9 @@ import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.NamedClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.user.User;
@ -360,8 +361,7 @@ public class RBACEngine implements AuthorizationEngine {
Map<String, Boolean> cluster = new HashMap<>();
for (String checkAction : request.clusterPrivileges()) {
final ClusterPrivilege checkPrivilege = ClusterPrivilege.get(Collections.singleton(checkAction));
cluster.put(checkAction, userRole.grants(checkPrivilege));
cluster.put(checkAction, userRole.grants(ClusterPrivilegeResolver.resolve(checkAction)));
}
boolean allMatch = cluster.values().stream().allMatch(Boolean::booleanValue);
ResourcePrivilegesMap.Builder combineIndicesResourcePrivileges = ResourcePrivilegesMap.builder();
@ -412,15 +412,17 @@ public class RBACEngine implements AuthorizationEngine {
// We use sorted sets for Strings because they will typically be small, and having a predictable order allows for simpler testing
final Set<String> cluster = new TreeSet<>();
// But we don't have a meaningful ordering for objects like ConditionalClusterPrivilege, so the tests work with "random" ordering
final Set<ConditionalClusterPrivilege> conditionalCluster = new HashSet<>();
for (Tuple<ClusterPrivilege, ConditionalClusterPrivilege> tup : userRole.cluster().privileges()) {
if (tup.v2() == null) {
if (ClusterPrivilege.NONE.equals(tup.v1()) == false) {
cluster.addAll(tup.v1().name());
}
// But we don't have a meaningful ordering for objects like ConfigurableClusterPrivilege, so the tests work with "random" ordering
final Set<ConfigurableClusterPrivilege> conditionalCluster = new HashSet<>();
for (ClusterPrivilege privilege : userRole.cluster().privileges()) {
if (privilege instanceof NamedClusterPrivilege) {
cluster.add(((NamedClusterPrivilege) privilege).name());
} else if (privilege instanceof ConfigurableClusterPrivilege) {
conditionalCluster.add((ConfigurableClusterPrivilege) privilege);
} else {
conditionalCluster.add(tup.v2());
throw new IllegalArgumentException(
"found unsupported cluster privilege : " + privilege +
((privilege != null) ? " of type " + privilege.getClass().getSimpleName() : ""));
}
}

View File

@ -35,7 +35,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDe
import org.elasticsearch.xpack.core.security.authz.permission.LimitedRole;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
@ -342,7 +342,7 @@ public class CompositeRolesStore {
}
Set<String> clusterPrivileges = new HashSet<>();
final List<ConditionalClusterPrivilege> conditionalClusterPrivileges = new ArrayList<>();
final List<ConfigurableClusterPrivilege> configurableClusterPrivileges = new ArrayList<>();
Set<String> runAs = new HashSet<>();
final Map<Set<String>, MergeableIndicesPrivilege> restrictedIndicesPrivilegesMap = new HashMap<>();
final Map<Set<String>, MergeableIndicesPrivilege> indicesPrivilegesMap = new HashMap<>();
@ -357,7 +357,7 @@ public class CompositeRolesStore {
clusterPrivileges.addAll(Arrays.asList(descriptor.getClusterPrivileges()));
}
if (descriptor.getConditionalClusterPrivileges() != null) {
conditionalClusterPrivileges.addAll(Arrays.asList(descriptor.getConditionalClusterPrivileges()));
configurableClusterPrivileges.addAll(Arrays.asList(descriptor.getConditionalClusterPrivileges()));
}
if (descriptor.getRunAs() != null) {
runAs.addAll(Arrays.asList(descriptor.getRunAs()));
@ -379,7 +379,7 @@ public class CompositeRolesStore {
final Privilege runAsPrivilege = runAs.isEmpty() ? Privilege.NONE : new Privilege(runAs, runAs.toArray(Strings.EMPTY_ARRAY));
final Role.Builder builder = Role.builder(roleNames.toArray(new String[roleNames.size()]))
.cluster(clusterPrivileges, conditionalClusterPrivileges)
.cluster(clusterPrivileges, configurableClusterPrivileges)
.runAs(runAsPrivilege);
indicesPrivilegesMap.entrySet().forEach((entry) -> {
MergeableIndicesPrivilege privilege = entry.getValue();

View File

@ -24,8 +24,8 @@ import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesRequestBuilder;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.client.SecurityClient;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.rest.action.SecurityBaseRestHandler;
@ -82,8 +82,8 @@ public class RestGetUserPrivilegesAction extends SecurityBaseRestHandler {
builder.field(RoleDescriptor.Fields.CLUSTER.getPreferredName(), response.getClusterPrivileges());
builder.startArray(RoleDescriptor.Fields.GLOBAL.getPreferredName());
for (ConditionalClusterPrivilege ccp : response.getConditionalClusterPrivileges()) {
ConditionalClusterPrivileges.toXContent(builder, ToXContent.EMPTY_PARAMS, Collections.singleton(ccp));
for (ConfigurableClusterPrivilege ccp : response.getConditionalClusterPrivileges()) {
ConfigurableClusterPrivileges.toXContent(builder, ToXContent.EMPTY_PARAMS, Collections.singleton(ccp));
}
builder.endArray();

View File

@ -80,10 +80,12 @@ import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.TriFunction;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.license.XPackLicenseState;
@ -110,13 +112,16 @@ import org.elasticsearch.xpack.core.security.authz.ResolvedIndices;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ActionClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
@ -134,7 +139,6 @@ import org.elasticsearch.xpack.sql.action.SqlQueryRequest;
import org.junit.Before;
import org.mockito.ArgumentMatcher;
import org.mockito.Matchers;
import org.mockito.Mockito;
import java.io.IOException;
import java.io.UncheckedIOException;
@ -315,15 +319,21 @@ public class AuthorizationServiceTests extends ESTestCase {
final DeletePrivilegesRequest request = new DeletePrivilegesRequest();
final Authentication authentication = createAuthentication(new User("user1", "role1"));
final ConditionalClusterPrivilege conditionalClusterPrivilege = Mockito.mock(ConditionalClusterPrivilege.class);
final Predicate<TransportRequest> requestPredicate = r -> r == request;
Mockito.when(conditionalClusterPrivilege.getRequestPredicate()).thenReturn(requestPredicate);
Mockito.when(conditionalClusterPrivilege.getPrivilege()).thenReturn(ClusterPrivilege.MANAGE_SECURITY);
final ConditionalClusterPrivilege[] conditionalClusterPrivileges = new ConditionalClusterPrivilege[] {
conditionalClusterPrivilege
final ConfigurableClusterPrivilege configurableClusterPrivilege = new MockConfigurableClusterPrivilege() {
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
final Predicate<TransportRequest> requestPredicate = r -> r == request;
final Predicate<String> actionPredicate =
Automatons.predicate(((ActionClusterPrivilege) ClusterPrivilegeResolver.MANAGE_SECURITY).getAllowedActionPatterns());
builder.add(this, actionPredicate, requestPredicate);
return builder;
}
};
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = new ConfigurableClusterPrivilege[] {
configurableClusterPrivilege
};
final String requestId = AuditUtil.getOrGenerateRequestId(threadContext);
RoleDescriptor role = new RoleDescriptor("role1", null, null, null, conditionalClusterPrivileges, null, null ,null);
RoleDescriptor role = new RoleDescriptor("role1", null, null, null, configurableClusterPrivileges, null, null ,null);
roleMap.put("role1", role);
authorize(authentication, DeletePrivilegesAction.NAME, request);
@ -336,15 +346,21 @@ public class AuthorizationServiceTests extends ESTestCase {
final DeletePrivilegesRequest request = new DeletePrivilegesRequest();
final Authentication authentication = createAuthentication(new User("user1", "role1"));
final ConditionalClusterPrivilege conditionalClusterPrivilege = Mockito.mock(ConditionalClusterPrivilege.class);
final Predicate<TransportRequest> requestPredicate = r -> false;
Mockito.when(conditionalClusterPrivilege.getRequestPredicate()).thenReturn(requestPredicate);
Mockito.when(conditionalClusterPrivilege.getPrivilege()).thenReturn(ClusterPrivilege.MANAGE_SECURITY);
final ConditionalClusterPrivilege[] conditionalClusterPrivileges = new ConditionalClusterPrivilege[] {
conditionalClusterPrivilege
final ConfigurableClusterPrivilege configurableClusterPrivilege = new MockConfigurableClusterPrivilege() {
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
final Predicate<TransportRequest> requestPredicate = r -> false;
final Predicate<String> actionPredicate =
Automatons.predicate(((ActionClusterPrivilege) ClusterPrivilegeResolver.MANAGE_SECURITY).getAllowedActionPatterns());
builder.add(this, actionPredicate,requestPredicate);
return builder;
}
};
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = new ConfigurableClusterPrivilege[] {
configurableClusterPrivilege
};
final String requestId = AuditUtil.getOrGenerateRequestId(threadContext);
RoleDescriptor role = new RoleDescriptor("role1", null, null, null, conditionalClusterPrivileges, null, null ,null);
RoleDescriptor role = new RoleDescriptor("role1", null, null, null, configurableClusterPrivileges, null, null ,null);
roleMap.put("role1", role);
assertThrowsAuthorizationException(
@ -1524,4 +1540,25 @@ public class AuthorizationServiceTests extends ESTestCase {
return false;
}
}
private abstract static class MockConfigurableClusterPrivilege implements ConfigurableClusterPrivilege {
@Override
public Category getCategory() {
return Category.APPLICATION;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder;
}
@Override
public String getWriteableName() {
return "mock";
}
@Override
public void writeTo(StreamOutput out) throws IOException {
}
}
}

View File

@ -19,18 +19,18 @@ import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivile
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.contains;
public class AuthorizedIndicesTests extends ESTestCase {
@ -88,7 +88,8 @@ public class AuthorizedIndicesTests extends ESTestCase {
}
public void testSecurityIndicesAreRemovedFromRegularUser() {
Role role = Role.builder("user_role").add(IndexPrivilege.ALL, "*").cluster(ClusterPrivilege.ALL).build();
Role role = Role.builder("user_role").add(IndexPrivilege.ALL, "*").cluster(Collections.singleton("all"), Collections.emptySet())
.build();
List<String> authorizedIndices =
RBACEngine.resolveAuthorizedIndicesFromRole(role, SearchAction.NAME, MetaData.EMPTY_META_DATA.getAliasAndIndexLookup());
assertTrue(authorizedIndices.isEmpty());
@ -97,7 +98,7 @@ public class AuthorizedIndicesTests extends ESTestCase {
public void testSecurityIndicesAreRestrictedForDefaultRole() {
Role role = Role.builder(randomFrom("user_role", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName()))
.add(IndexPrivilege.ALL, "*")
.cluster(ClusterPrivilege.ALL)
.cluster(Collections.singleton("all"), Collections.emptySet())
.build();
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6,
@ -124,7 +125,7 @@ public class AuthorizedIndicesTests extends ESTestCase {
public void testSecurityIndicesAreNotRemovedFromUnrestrictedRole() {
Role role = Role.builder(randomAlphaOfLength(8))
.add(FieldPermissions.DEFAULT, null, IndexPrivilege.ALL, true, "*")
.cluster(ClusterPrivilege.ALL)
.cluster(Collections.singleton("all"), Collections.emptySet())
.build();
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
final String internalSecurityIndex = randomFrom(RestrictedIndicesNames.INTERNAL_SECURITY_MAIN_INDEX_6,

View File

@ -46,11 +46,10 @@ import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges.ManageApplicationPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authz.RBACEngine.RBACAuthorizationInfo;
@ -284,7 +283,7 @@ public class RBACEngineTests extends ESTestCase {
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
Role role = Role.builder("test2")
.cluster(ClusterPrivilege.MONITOR)
.cluster(Collections.singleton("monitor"), Collections.emptySet())
.add(IndexPrivilege.INDEX, "academy")
.add(IndexPrivilege.WRITE, "initiative")
.build();
@ -343,7 +342,7 @@ public class RBACEngineTests extends ESTestCase {
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
Role role = Role.builder("test3")
.cluster(ClusterPrivilege.MONITOR)
.cluster(Collections.singleton("monitor"), Collections.emptySet())
.build();
RBACAuthorizationInfo authzInfo = new RBACAuthorizationInfo(role, null);
@ -729,7 +728,7 @@ public class RBACEngineTests extends ESTestCase {
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
Role role = Role.builder("test-write")
.cluster(ClusterPrivilege.MONITOR)
.cluster(Collections.singleton("monitor"), Collections.emptySet())
.add(IndexPrivilege.READ, "read-*")
.add(IndexPrivilege.ALL, "all-*")
.addApplicationPrivilege(kibanaRead, Collections.singleton("*"))

View File

@ -22,8 +22,8 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.xpack.core.XPackClientPlugin;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.support.MetadataUtils;
import org.hamcrest.Matchers;
@ -72,12 +72,12 @@ public class RoleDescriptorTests extends ESTestCase {
.build()
};
final ConditionalClusterPrivilege[] conditionalClusterPrivileges = new ConditionalClusterPrivilege[]{
new ConditionalClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = new ConfigurableClusterPrivilege[]{
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
};
RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, applicationPrivileges,
conditionalClusterPrivileges, new String[] { "sudo" }, Collections.emptyMap(), Collections.emptyMap());
configurableClusterPrivileges, new String[] { "sudo" }, Collections.emptyMap(), Collections.emptyMap());
assertThat(descriptor.toString(), is("Role[name=test, cluster=[all,none]" +
", global=[{APPLICATION:manage:applications=app01,app02}]" +
@ -104,13 +104,13 @@ public class RoleDescriptorTests extends ESTestCase {
.resources("*")
.build()
};
final ConditionalClusterPrivilege[] conditionalClusterPrivileges = {
new ConditionalClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
};
Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
RoleDescriptor descriptor = new RoleDescriptor("test", new String[] { "all", "none" }, groups, applicationPrivileges,
conditionalClusterPrivileges, new String[]{ "sudo" }, metadata, Collections.emptyMap());
configurableClusterPrivileges, new String[]{ "sudo" }, metadata, Collections.emptyMap());
XContentBuilder builder = descriptor.toXContent(jsonBuilder(), ToXContent.EMPTY_PARAMS);
RoleDescriptor parsed = RoleDescriptor.parse("test", BytesReference.bytes(builder), false, XContentType.JSON);
assertThat(parsed, equalTo(descriptor));
@ -189,10 +189,10 @@ public class RoleDescriptorTests extends ESTestCase {
assertThat(rd.getApplicationPrivileges()[1].getApplication(), equalTo("app2"));
assertThat(rd.getConditionalClusterPrivileges(), Matchers.arrayWithSize(1));
final ConditionalClusterPrivilege conditionalPrivilege = rd.getConditionalClusterPrivileges()[0];
assertThat(conditionalPrivilege.getCategory(), equalTo(ConditionalClusterPrivilege.Category.APPLICATION));
assertThat(conditionalPrivilege, instanceOf(ConditionalClusterPrivileges.ManageApplicationPrivileges.class));
assertThat(((ConditionalClusterPrivileges.ManageApplicationPrivileges) conditionalPrivilege).getApplicationNames(),
final ConfigurableClusterPrivilege conditionalPrivilege = rd.getConditionalClusterPrivileges()[0];
assertThat(conditionalPrivilege.getCategory(), equalTo(ConfigurableClusterPrivilege.Category.APPLICATION));
assertThat(conditionalPrivilege, instanceOf(ConfigurableClusterPrivileges.ManageApplicationPrivileges.class));
assertThat(((ConfigurableClusterPrivileges.ManageApplicationPrivileges) conditionalPrivilege).getApplicationNames(),
containsInAnyOrder("kibana", "logstash"));
q = "{\"applications\": [{\"application\": \"myapp\", \"resources\": [\"*\"], \"privileges\": [\"login\" ]}] }";
@ -233,13 +233,13 @@ public class RoleDescriptorTests extends ESTestCase {
.resources("*")
.build()
};
final ConditionalClusterPrivilege[] conditionalClusterPrivileges = {
new ConditionalClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
final ConfigurableClusterPrivilege[] configurableClusterPrivileges = {
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02")))
};
Map<String, Object> metadata = randomBoolean() ? MetadataUtils.DEFAULT_RESERVED_METADATA : null;
final RoleDescriptor descriptor = new RoleDescriptor("test", new String[]{"all", "none"}, groups, applicationPrivileges,
conditionalClusterPrivileges, new String[] { "sudo" }, metadata, null);
configurableClusterPrivileges, new String[] { "sudo" }, metadata, null);
descriptor.writeTo(output);
final NamedWriteableRegistry registry = new NamedWriteableRegistry(new XPackClientPlugin(Settings.EMPTY).getNamedWriteables());
StreamInput streamInput = new NamedWriteableAwareStreamInput(ByteBufferStreamInput.wrap(BytesReference.toBytes(output.bytes())),

View File

@ -16,9 +16,11 @@ import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
@ -37,16 +39,19 @@ import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ActionClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.User;
@ -75,11 +80,11 @@ import java.util.function.Predicate;
import static org.elasticsearch.mock.orig.Mockito.times;
import static org.elasticsearch.mock.orig.Mockito.verifyNoMoreInteractions;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anySetOf;
import static org.mockito.Matchers.eq;
@ -542,9 +547,15 @@ public class CompositeRolesStoreTests extends ESTestCase {
final TransportRequest request2 = mock(TransportRequest.class);
final TransportRequest request3 = mock(TransportRequest.class);
ConditionalClusterPrivilege ccp1 = mock(ConditionalClusterPrivilege.class);
when(ccp1.getPrivilege()).thenReturn(ClusterPrivilege.MANAGE_SECURITY);
when(ccp1.getRequestPredicate()).thenReturn(req -> req == request1);
ConfigurableClusterPrivilege ccp1 = new MockConfigurableClusterPrivilege() {
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
Predicate<String> predicate1 =
Automatons.predicate(((ActionClusterPrivilege) ClusterPrivilegeResolver.MANAGE_SECURITY).getAllowedActionPatterns());
builder.add(this, predicate1, req -> req == request1);
return builder;
}
};
RoleDescriptor role1 = new RoleDescriptor("r1", new String[]{"monitor"}, new IndicesPrivileges[]{
IndicesPrivileges.builder()
.indices("abc-*", "xyz-*")
@ -565,12 +576,18 @@ public class CompositeRolesStoreTests extends ESTestCase {
.resources("settings/*")
.privileges("read")
.build()
}, new ConditionalClusterPrivilege[] { ccp1 },
}, new ConfigurableClusterPrivilege[] { ccp1 },
new String[]{"app-user-1"}, null, null);
ConditionalClusterPrivilege ccp2 = mock(ConditionalClusterPrivilege.class);
when(ccp2.getPrivilege()).thenReturn(ClusterPrivilege.MANAGE_SECURITY);
when(ccp2.getRequestPredicate()).thenReturn(req -> req == request2);
ConfigurableClusterPrivilege ccp2 = new MockConfigurableClusterPrivilege() {
@Override
public ClusterPermission.Builder buildPermission(ClusterPermission.Builder builder) {
Predicate<String> predicate2 =
Automatons.predicate(((ActionClusterPrivilege) ClusterPrivilegeResolver.MANAGE_SECURITY).getAllowedActionPatterns());
builder.add(this, predicate2, req -> req == request2);
return builder;
}
};
RoleDescriptor role2 = new RoleDescriptor("r2", new String[]{"manage_saml"}, new IndicesPrivileges[]{
IndicesPrivileges.builder()
.indices("abc-*", "ind-2-*")
@ -587,7 +604,7 @@ public class CompositeRolesStoreTests extends ESTestCase {
.resources("*")
.privileges("read")
.build()
}, new ConditionalClusterPrivilege[] { ccp2 },
}, new ConfigurableClusterPrivilege[] { ccp2 },
new String[]{"app-user-2"}, null, null);
FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY);
@ -1076,4 +1093,25 @@ public class CompositeRolesStoreTests extends ESTestCase {
listener.onResponse(roleDescriptorsFunc.apply(roles));
}
}
private abstract static class MockConfigurableClusterPrivilege implements ConfigurableClusterPrivilege {
@Override
public Category getCategory() {
return Category.APPLICATION;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
return builder;
}
@Override
public String getWriteableName() {
return "mock";
}
@Override
public void writeTo(StreamOutput out) throws IOException {
}
}
}

View File

@ -25,7 +25,7 @@ import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.permission.RunAsPermission;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver;
import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege;
import java.io.BufferedWriter;
@ -74,7 +74,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role1" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL));
assertTrue(role.cluster().implies(ClusterPrivilegeResolver.ALL.buildPermission(ClusterPermission.builder()).build()));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(2));
@ -102,7 +102,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role1.ab" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL));
assertTrue(role.cluster().implies(ClusterPrivilegeResolver.ALL.buildPermission(ClusterPermission.builder()).build()));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(0));
@ -114,7 +114,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role2" }));
assertThat(role.cluster(), notNullValue());
assertTrue(Operations.sameLanguage(role.cluster().privilege().getAutomaton(), ClusterPrivilege.ALL.getAutomaton()));
assertTrue(role.cluster().implies(ClusterPrivilegeResolver.ALL.buildPermission(ClusterPermission.builder()).build()));
assertThat(role.indices(), notNullValue());
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
@ -125,7 +125,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role3" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -149,7 +149,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role_run_as" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
@ -162,7 +162,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role_run_as1" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
@ -175,7 +175,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role_fields" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
@ -197,7 +197,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role_query" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
@ -218,7 +218,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.names(), equalTo(new String[] { "role_query_fields" }));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.SimpleClusterPermission.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());

View File

@ -22,8 +22,8 @@ import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.ApplicationResourcePrivileges;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivileges;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import java.util.Arrays;
import java.util.Collections;
@ -55,8 +55,8 @@ public class RestGetUserPrivilegesActionTests extends ESTestCase {
public void testBuildResponse() throws Exception {
final RestGetUserPrivilegesAction.RestListener listener = new RestGetUserPrivilegesAction.RestListener(null);
final Set<String> cluster = new LinkedHashSet<>(Arrays.asList("monitor", "manage_ml", "manage_watcher"));
final Set<ConditionalClusterPrivilege> conditionalCluster = Collections.singleton(
new ConditionalClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02"))));
final Set<ConfigurableClusterPrivilege> conditionalCluster = Collections.singleton(
new ConfigurableClusterPrivileges.ManageApplicationPrivileges(new LinkedHashSet<>(Arrays.asList("app01", "app02"))));
final Set<GetUserPrivilegesResponse.Indices> index = new LinkedHashSet<>(Arrays.asList(
new GetUserPrivilegesResponse.Indices(Arrays.asList("index-1", "index-2", "index-3-*"), Arrays.asList("read", "write"),
new LinkedHashSet<>(Arrays.asList(