All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission Maven / Gradle / Ivy

/*
 * 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; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy