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

org.elasticsearch.xpack.core.security.authz.permission.ApplicationPermission 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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.support.Automatons;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * A permission that is based on privileges for application (non elasticsearch) capabilities
 */
public final class ApplicationPermission {

    public static final ApplicationPermission NONE = new ApplicationPermission(Collections.emptyList());

    private final Logger logger;
    private final List permissions;

    /**
     * @param privilegesAndResources A list of (privilege, resources). Each element in the {@link List} is a {@link Tuple} containing
     *                               a single {@link ApplicationPrivilege} and the {@link Set} of resources to which that privilege is
     *                               applied. The resources are treated as a wildcard {@link Automatons#pattern}.
     */
    ApplicationPermission(List>> privilegesAndResources) {
        this.logger = LogManager.getLogger(getClass());
        Map permissionsByPrivilege = new HashMap<>();
        privilegesAndResources.forEach(tup -> permissionsByPrivilege.compute(tup.v1(), (appPriv, existing) -> {
            final Set resourceNames = tup.v2();
            final Automaton patterns = Automatons.patterns(resourceNames);
            if (existing == null) {
                return new PermissionEntry(appPriv, resourceNames, patterns);
            } else {
                return new PermissionEntry(appPriv, Sets.union(existing.resourceNames, resourceNames),
                    Automatons.unionAndMinimize(Arrays.asList(existing.resourceAutomaton, patterns)));
            }
        }));
        this.permissions = Collections.unmodifiableList(new ArrayList<>(permissionsByPrivilege.values()));
    }

    /**
     * Determines whether this permission grants the specified privilege on the given resource.
     * 

* An {@link ApplicationPermission} consists of a sequence of permission entries, where each entry contains a single * {@link ApplicationPrivilege} and one or more resource patterns. *

*

* This method returns {@code true} if, one or more of those entries meet the following criteria *

*
    *
  • The entry's application, when interpreted as an {@link Automaton} {@link Automatons#pattern(String) pattern} matches the * application given in the argument (interpreted as a raw string) *
  • *
  • The {@link ApplicationPrivilege#getAutomaton automaton that defines the entry's actions} entirely covers the * automaton given in the argument (that is, the argument is a subset of the entry's automaton) *
  • *
  • The entry's resources, when interpreted as an {@link Automaton} {@link Automatons#patterns(String...)} set of patterns} entirely * covers the resource given in the argument (also interpreted as an {@link Automaton} {@link Automatons#pattern(String) pattern}. *
  • *
*/ public boolean grants(ApplicationPrivilege other, String resource) { Automaton resourceAutomaton = Automatons.patterns(resource); final boolean matched = permissions.stream().anyMatch(e -> e.grants(other, resourceAutomaton)); logger.trace("Permission [{}] {} grant [{} , {}]", this, matched ? "does" : "does not", other, resource); return matched; } /** * For a given application, checks for the privileges for resources and returns an instance of {@link ResourcePrivilegesMap} holding a * map of resource to {@link ResourcePrivileges} where the resource is application resource and the map of application privilege to * whether it is allowed or not. * * @param applicationName checks privileges for the provided application name * @param checkForResources check permission grants for the set of resources * @param checkForPrivilegeNames check permission grants for the set of privilege names * @param storedPrivileges stored {@link ApplicationPrivilegeDescriptor} for an application against which the access checks are * performed * @return an instance of {@link ResourcePrivilegesMap} */ public ResourcePrivilegesMap checkResourcePrivileges(final String applicationName, Set checkForResources, Set checkForPrivilegeNames, Collection storedPrivileges) { final ResourcePrivilegesMap.Builder resourcePrivilegesMapBuilder = ResourcePrivilegesMap.builder(); for (String checkResource : checkForResources) { for (String checkPrivilegeName : checkForPrivilegeNames) { final Set nameSet = Collections.singleton(checkPrivilegeName); final Set checkPrivileges = ApplicationPrivilege.get(applicationName, nameSet, storedPrivileges); logger.trace("Resolved privileges [{}] for [{},{}]", checkPrivileges, applicationName, nameSet); for (ApplicationPrivilege checkPrivilege : checkPrivileges) { assert Automatons.predicate(applicationName).test(checkPrivilege.getApplication()) : "Privilege " + checkPrivilege + " should have application " + applicationName; assert checkPrivilege.name().equals(nameSet) : "Privilege " + checkPrivilege + " should have name " + nameSet; if (grants(checkPrivilege, checkResource)) { resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.TRUE); } else { resourcePrivilegesMapBuilder.addResourcePrivilege(checkResource, checkPrivilegeName, Boolean.FALSE); } } } } return resourcePrivilegesMapBuilder.build(); } @Override public String toString() { return getClass().getSimpleName() + "{privileges=" + permissions + "}"; } public Set getApplicationNames() { return permissions.stream() .map(e -> e.privilege.getApplication()) .collect(Collectors.toSet()); } public Set getPrivileges(String application) { return permissions.stream() .filter(e -> application.equals(e.privilege.getApplication())) .map(e -> e.privilege) .collect(Collectors.toSet()); } /** * Returns a set of resource patterns that are permitted for the provided privilege. * The returned set may include patterns that overlap (e.g. "object/*" and "object/1") and may * also include patterns that are defined again a more permissive privilege. * e.g. If a permission grants *
    *
  • "my-app", "read", [ "user/*" ]
  • *
  • "my-app", "all", [ "user/kimchy", "config/*" ]
  • *
* Then getResourcePatterns( myAppRead ) would return "user/*", "user/kimchy", "config/*". */ public Set getResourcePatterns(ApplicationPrivilege privilege) { return permissions.stream() .filter(e -> e.matchesPrivilege(privilege)) .map(e -> e.resourceNames) .flatMap(Set::stream) .collect(Collectors.toSet()); } private static class PermissionEntry { private final ApplicationPrivilege privilege; private final Predicate application; private final Set resourceNames; private final Automaton resourceAutomaton; private PermissionEntry(ApplicationPrivilege privilege, Set resourceNames, Automaton resourceAutomaton) { this.privilege = privilege; this.application = Automatons.predicate(privilege.getApplication()); this.resourceNames = resourceNames; this.resourceAutomaton = resourceAutomaton; } private boolean grants(ApplicationPrivilege other, Automaton resource) { return matchesPrivilege(other) && Operations.subsetOf(resource, this.resourceAutomaton); } private boolean matchesPrivilege(ApplicationPrivilege other) { if (this.privilege.equals(other)) { return true; } if (this.application.test(other.getApplication()) == false) { return false; } if (Operations.isTotal(privilege.getAutomaton())) { return true; } return Operations.isEmpty(privilege.getAutomaton()) == false && Operations.isEmpty(other.getAutomaton()) == false && Operations.subsetOf(other.getAutomaton(), privilege.getAutomaton()); } @Override public String toString() { return privilege.toString() + ":" + resourceNames; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy