Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
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.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
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;
/**
* A permission that is based on privileges for cluster wide actions, with the optional ability to inspect the request object
*/
public class ClusterPermission {
public static final ClusterPermission NONE = new ClusterPermission(Collections.emptySet(), Collections.emptyList());
private final Set clusterPrivileges;
private final List checks;
private ClusterPermission(final Set clusterPrivileges,
final List checks) {
this.clusterPrivileges = Collections.unmodifiableSet(clusterPrivileges);
this.checks = Collections.unmodifiableList(checks);
}
/**
* Checks permission to a cluster action for a given request in the context of given
* authentication.
*
* @param action cluster action
* @param request {@link TransportRequest}
* @param authentication {@link Authentication}
* @return {@code true} if the access is allowed else returns {@code false}
*/
public boolean check(final String action, final TransportRequest request, final Authentication authentication) {
return checks.stream().anyMatch(permission -> permission.check(action, request, authentication));
}
/**
* 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;
}
}
public Set privileges() {
return clusterPrivileges;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private final Set clusterPrivileges = new HashSet<>();
private final List actionAutomatons = new ArrayList<>();
private final List permissionChecks = new ArrayList<>();
public Builder add(final ClusterPrivilege clusterPrivilege, final Set allowedActionPatterns,
final Set excludeActionPatterns) {
this.clusterPrivileges.add(clusterPrivilege);
final Automaton actionAutomaton = createAutomaton(allowedActionPatterns, excludeActionPatterns);
this.actionAutomatons.add(actionAutomaton);
return this;
}
public Builder add(final ClusterPrivilege clusterPrivilege, final Set allowedActionPatterns,
final Predicate requestPredicate) {
final Automaton actionAutomaton = createAutomaton(allowedActionPatterns, Collections.emptySet());
return add(clusterPrivilege, new ActionRequestBasedPermissionCheck(clusterPrivilege, actionAutomaton, requestPredicate));
}
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 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);
}
private static Automaton createAutomaton(Set allowedActionPatterns, Set excludeActionPatterns) {
allowedActionPatterns = (allowedActionPatterns == null) ? Collections.emptySet() : allowedActionPatterns;
excludeActionPatterns = (excludeActionPatterns == null) ? Collections.emptySet() : excludeActionPatterns;
if (allowedActionPatterns.isEmpty()) {
return Automatons.EMPTY;
} else if (excludeActionPatterns.isEmpty()) {
return Automatons.patterns(allowedActionPatterns);
} else {
final Automaton allowedAutomaton = Automatons.patterns(allowedActionPatterns);
final Automaton excludedAutomaton = Automatons.patterns(excludeActionPatterns);
return Automatons.minusAndMinimize(allowedAutomaton, excludedAutomaton);
}
}
}
/**
* Evaluates whether the cluster actions (optionally for a given request)
* is permitted by this permission.
*/
public interface PermissionCheck {
/**
* Checks permission to a cluster action for a given request in the context of given
* authentication.
*
* @param action action name
* @param request {@link TransportRequest}
* @param authentication {@link Authentication}
* @return {@code true} if the specified action for given request is allowed else returns {@code false}
*/
boolean check(String action, TransportRequest request, Authentication authentication);
/**
* Checks whether specified {@link PermissionCheck} is implied by this {@link PermissionCheck}.
* 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);
}
/**
* Base for implementing cluster action based {@link PermissionCheck}.
* It enforces the checks at cluster action level and then hands it off to the implementations
* to enforce checks based on {@link TransportRequest} and/or {@link Authentication}.
*/
public abstract static class ActionBasedPermissionCheck implements PermissionCheck {
private final Automaton automaton;
private final Predicate actionPredicate;
public ActionBasedPermissionCheck(final Automaton automaton) {
this.automaton = automaton;
this.actionPredicate = Automatons.predicate(automaton);
}
@Override
public final boolean check(final String action, final TransportRequest request, final Authentication authentication) {
return actionPredicate.test(action) && extendedCheck(action, request, authentication);
}
protected abstract boolean extendedCheck(String action, TransportRequest request, Authentication authentication);
@Override
public final boolean implies(final PermissionCheck permissionCheck) {
if (permissionCheck instanceof ActionBasedPermissionCheck) {
return Operations.subsetOf(((ActionBasedPermissionCheck) permissionCheck).automaton, this.automaton) &&
doImplies((ActionBasedPermissionCheck) permissionCheck);
}
return false;
}
protected abstract boolean doImplies(ActionBasedPermissionCheck permissionCheck);
}
// Automaton based permission check
private static class AutomatonPermissionCheck extends ActionBasedPermissionCheck {
AutomatonPermissionCheck(final Automaton automaton) {
super(automaton);
}
@Override
protected boolean extendedCheck(String action, TransportRequest request, Authentication authentication) {
return true;
}
@Override
protected boolean doImplies(ActionBasedPermissionCheck permissionCheck) {
/*
* We know that "permissionCheck" has an automaton which is a subset of ours.
* Which means "permissionCheck" _cannot_ grant an action that we don't (see ActionBasedPermissionCheck#check)
* Since we grant _all_ requests on actions within our automaton, we must therefore grant _all_ actions+requests that the other
* permission check grants.
*/
return true;
}
}
// action, request based permission check
private static class ActionRequestBasedPermissionCheck extends ActionBasedPermissionCheck {
private final ClusterPrivilege clusterPrivilege;
private final Predicate requestPredicate;
ActionRequestBasedPermissionCheck(ClusterPrivilege clusterPrivilege, final Automaton automaton,
final Predicate requestPredicate) {
super(automaton);
this.requestPredicate = requestPredicate;
this.clusterPrivilege = clusterPrivilege;
}
@Override
protected boolean extendedCheck(String action, TransportRequest request, Authentication authentication) {
return requestPredicate.test(request);
}
@Override
protected boolean doImplies(final ActionBasedPermissionCheck permissionCheck) {
if (permissionCheck instanceof ActionRequestBasedPermissionCheck) {
final ActionRequestBasedPermissionCheck otherCheck =
(ActionRequestBasedPermissionCheck) permissionCheck;
return this.clusterPrivilege.equals(otherCheck.clusterPrivilege);
}
return false;
}
}
}