Skip to content

Commit

Permalink
Add support for authentication based predicate for cluster permission
Browse files Browse the repository at this point in the history
Currently, cluster permission checks whether a cluster action is
permitted and optionally in the context of a request. There are
scenarios where we would want to check whether the cluster action
is permitted, optionally in the context of a request and current
authentication. For example, management of API keys is only
restricted to the API keys owned by the current user. In this case,
along with the cluster action and API key request, the check
needs to perform whether the currently authenticated user is indeed
allowed to operate only on owned API keys.
With this commit, we are introducing one more context of the current
authentication that can be considered during permission evaluation.

Relates: elastic#40031
  • Loading branch information
Yogesh Gaikwad committed Aug 12, 2019
1 parent 9170c81 commit 80fa13f
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 371 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
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.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
Expand All @@ -34,14 +35,16 @@ private ClusterPermission(final Set<ClusterPrivilege> clusterPrivileges,
}

/**
* Checks permission to a cluster action for a given request.
* 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) {
return checks.stream().anyMatch(permission -> permission.check(action, request));
public boolean check(final String action, final TransportRequest request, final Authentication authentication) {
return checks.stream().anyMatch(permission -> permission.check(action, request, authentication));
}

/**
Expand Down Expand Up @@ -90,11 +93,10 @@ public Builder add(final ClusterPrivilege clusterPrivilege, final Set<String> al
return this;
}

public Builder add(final ConfigurableClusterPrivilege configurableClusterPrivilege, final Predicate<String> actionPredicate,
final Predicate<TransportRequest> requestPredicate) {
return add(configurableClusterPrivilege, new ActionRequestPredicatePermissionCheck(configurableClusterPrivilege,
actionPredicate,
requestPredicate));
public Builder add(final ClusterPrivilege clusterPrivilege, final Predicate<String> actionPredicate,
final BiPredicate<TransportRequest, Authentication> requestAuthnPredicate) {
return add(clusterPrivilege, new ActionRequestAuthenticationPredicatePermissionCheck(clusterPrivilege,
actionPredicate, requestAuthnPredicate));
}

public Builder add(final ClusterPrivilege clusterPrivilege, final PermissionCheck permissionCheck) {
Expand Down Expand Up @@ -124,13 +126,15 @@ public ClusterPermission build() {
*/
public interface PermissionCheck {
/**
* Checks permission to a cluster action for a given request.
* 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);
boolean check(String action, TransportRequest request, Authentication authentication);

/**
* Checks whether specified {@link PermissionCheck} is implied by this {@link PermissionCheck}.<br>
Expand All @@ -156,7 +160,7 @@ private static class AutomatonPermissionCheck implements PermissionCheck {
}

@Override
public boolean check(final String action, final TransportRequest request) {
public boolean check(final String action, final TransportRequest request, final Authentication authentication) {
return actionPredicate.test(action);
}

Expand All @@ -169,28 +173,30 @@ public boolean implies(final PermissionCheck permissionCheck) {
}
}

// action and request based permission check
private static class ActionRequestPredicatePermissionCheck implements PermissionCheck {
// action, request and authentication based permission check
private static class ActionRequestAuthenticationPredicatePermissionCheck implements PermissionCheck {
private final ClusterPrivilege clusterPrivilege;
final Predicate<String> actionPredicate;
final Predicate<TransportRequest> requestPredicate;
final BiPredicate<TransportRequest, Authentication> requestAuthnPredicate;

ActionRequestPredicatePermissionCheck(final ClusterPrivilege clusterPrivilege, final Predicate<String> actionPredicate,
final Predicate<TransportRequest> requestPredicate) {
ActionRequestAuthenticationPredicatePermissionCheck(final ClusterPrivilege clusterPrivilege,
final Predicate<String> actionPredicate,
final BiPredicate<TransportRequest, Authentication> requestAuthnPredicate) {
this.clusterPrivilege = clusterPrivilege;
this.actionPredicate = actionPredicate;
this.requestPredicate = requestPredicate;
this.requestAuthnPredicate = requestAuthnPredicate;
}

@Override
public boolean check(final String action, final TransportRequest request) {
return actionPredicate.test(action) && requestPredicate.test(request);
public boolean check(final String action, final TransportRequest request, final Authentication authentication) {
return actionPredicate.test(action) && requestAuthnPredicate.test(request, authentication);
}

@Override
public boolean implies(final PermissionCheck permissionCheck) {
if (permissionCheck instanceof ActionRequestPredicatePermissionCheck) {
final ActionRequestPredicatePermissionCheck otherCheck = (ActionRequestPredicatePermissionCheck) permissionCheck;
if (permissionCheck instanceof ActionRequestAuthenticationPredicatePermissionCheck) {
final ActionRequestAuthenticationPredicatePermissionCheck otherCheck =
(ActionRequestAuthenticationPredicatePermissionCheck) permissionCheck;
return this.clusterPrivilege.equals(otherCheck.clusterPrivilege);
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
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;
Expand Down Expand Up @@ -122,15 +123,18 @@ public ResourcePrivilegesMap checkIndicesPrivileges(Set<String> checkForIndexPat
}

/**
* Check if cluster permissions allow for the given action, also checks whether the limited by role allows the given actions
* Check if cluster permissions allow for the given action,
* also checks whether the limited by role allows the given actions in the context of given
* authentication.
*
* @param action cluster action
* @param request {@link TransportRequest}
* @param authentication {@link Authentication}
* @return {@code true} if action is allowed else returns {@code false}
*/
@Override
public boolean checkClusterAction(String action, TransportRequest request) {
return super.checkClusterAction(action, request) && limitedBy.checkClusterAction(action, request);
public boolean checkClusterAction(String action, TransportRequest request, Authentication authentication) {
return super.checkClusterAction(action, request, authentication) && limitedBy.checkClusterAction(action, request, authentication);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
Expand Down Expand Up @@ -121,14 +122,16 @@ public ResourcePrivilegesMap checkIndicesPrivileges(Set<String> checkForIndexPat
}

/**
* Check if cluster permissions allow for the given action
* Check if cluster permissions allow for the given action in the context of given
* authentication.
*
* @param action cluster action
* @param request {@link TransportRequest}
* @param authentication {@link Authentication}
* @return {@code true} if action is allowed else returns {@code false}
*/
public boolean checkClusterAction(String action, TransportRequest request) {
return cluster.check(action, request);
public boolean checkClusterAction(String action, TransportRequest request, Authentication authentication) {
return cluster.check(action, request, authentication);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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.authc.Authentication;
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;
Expand All @@ -30,6 +31,7 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Predicate;

/**
Expand Down Expand Up @@ -131,12 +133,12 @@ public static class ManageApplicationPrivileges implements ConfigurableClusterPr

private final Set<String> applicationNames;
private final Predicate<String> applicationPredicate;
private final Predicate<TransportRequest> requestPredicate;
private final BiPredicate<TransportRequest, Authentication> requestAuthnPredicate;

public ManageApplicationPrivileges(Set<String> applicationNames) {
this.applicationNames = Collections.unmodifiableSet(applicationNames);
this.applicationPredicate = Automatons.predicate(applicationNames);
this.requestPredicate = request -> {
this.requestAuthnPredicate = (request, authn) -> {
if (request instanceof ApplicationPrivilegesRequest) {
final ApplicationPrivilegesRequest privRequest = (ApplicationPrivilegesRequest) request;
final Collection<String> requestApplicationNames = privRequest.getApplicationNames();
Expand Down Expand Up @@ -215,7 +217,7 @@ public int hashCode() {

@Override
public ClusterPermission.Builder buildPermission(final ClusterPermission.Builder builder) {
return builder.add(this, ACTION_PREDICATE, requestPredicate);
return builder.add(this, ACTION_PREDICATE, requestAuthnPredicate);
}

private interface Fields {
Expand Down
Loading

0 comments on commit 80fa13f

Please sign in to comment.