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

org.elasticsearch.xpack.core.security.authz.support.DLSRoleQueryValidator 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.support;

import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParseException;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.BoostingQueryBuilder;
import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.user.User;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * This class helps in evaluating the query field if it is template,
 * validating the query and checking if the query type is allowed to be used in DLS role query.
 */
public final class DLSRoleQueryValidator {

    private DLSRoleQueryValidator() {
    }

    /**
     * Validates the query field in the {@link RoleDescriptor.IndicesPrivileges} only if it is not a template query.
* It parses the query and builds the {@link QueryBuilder}, also checks if the query type is supported in DLS role query. * * @param indicesPrivileges {@link RoleDescriptor.IndicesPrivileges} * @param xContentRegistry {@link NamedXContentRegistry} for finding named queries */ public static void validateQueryField(RoleDescriptor.IndicesPrivileges[] indicesPrivileges, NamedXContentRegistry xContentRegistry) { if (indicesPrivileges != null) { for (int i = 0; i < indicesPrivileges.length; i++) { BytesReference query = indicesPrivileges[i].getQuery(); try { if (query != null) { if (isTemplateQuery(query, xContentRegistry)) { // skip template query, this requires runtime information like 'User' information. continue; } evaluateAndVerifyRoleQuery(query.utf8ToString(), xContentRegistry); } } catch (ParsingException | IllegalArgumentException | IOException e) { throw new ElasticsearchParseException("failed to parse field 'query' for indices [" + Strings.arrayToCommaDelimitedString(indicesPrivileges[i].getIndices()) + "] at index privilege [" + i + "] of role descriptor", e); } } } } private static boolean isTemplateQuery(BytesReference query, NamedXContentRegistry xContentRegistry) throws IOException { try (XContentParser parser = XContentType.JSON.xContent().createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, query.utf8ToString())) { XContentParser.Token token = parser.nextToken(); if (token != XContentParser.Token.START_OBJECT) { throw new XContentParseException(parser.getTokenLocation(), "expected [" + XContentParser.Token.START_OBJECT + "] but " + "found [" + token + "] instead"); } token = parser.nextToken(); if (token != XContentParser.Token.FIELD_NAME) { throw new XContentParseException(parser.getTokenLocation(), "expected [" + XContentParser.Token.FIELD_NAME + "] with " + "value a query name or 'template' but found [" + token + "] instead"); } String fieldName = parser.currentName(); if ("template".equals(fieldName)) { return true; } } return false; } /** * Evaluates the query if it is a template and then validates the query by parsing * and building the {@link QueryBuilder}. It also checks if the query type is * supported in DLS role query. * * @param query {@link BytesReference} query field from the role * @param scriptService {@link ScriptService} used for evaluation of a template query * @param xContentRegistry {@link NamedXContentRegistry} for finding named queries * @param user {@link User} used when evaluation a template query * @return {@link QueryBuilder} if the query is valid and allowed, in case {@link RoleDescriptor.IndicesPrivileges} * * does not have a query field then it returns {@code null}. */ @Nullable public static QueryBuilder evaluateAndVerifyRoleQuery(BytesReference query, ScriptService scriptService, NamedXContentRegistry xContentRegistry, User user) { if (query != null) { String templateResult = SecurityQueryTemplateEvaluator.evaluateTemplate(query.utf8ToString(), scriptService, user); try { return evaluateAndVerifyRoleQuery(templateResult, xContentRegistry); } catch (ElasticsearchParseException | ParsingException | XContentParseException | IOException e) { throw new ElasticsearchParseException("failed to parse field 'query' from the role descriptor", e); } } return null; } @Nullable private static QueryBuilder evaluateAndVerifyRoleQuery(String query, NamedXContentRegistry xContentRegistry) throws IOException { if (query != null) { try (XContentParser parser = XContentFactory.xContent(query).createParser(xContentRegistry, LoggingDeprecationHandler.INSTANCE, query)) { QueryBuilder queryBuilder = AbstractQueryBuilder.parseInnerQueryBuilder(parser); verifyRoleQuery(queryBuilder); return queryBuilder; } } return null; } /** * Checks whether the role query contains queries we know can't be used as DLS role query. * * @param queryBuilder {@link QueryBuilder} for given query */ // pkg protected for testing static void verifyRoleQuery(QueryBuilder queryBuilder) { if (queryBuilder instanceof TermsQueryBuilder) { TermsQueryBuilder termsQueryBuilder = (TermsQueryBuilder) queryBuilder; if (termsQueryBuilder.termsLookup() != null) { throw new IllegalArgumentException("terms query with terms lookup isn't supported as part of a role query"); } } else if (queryBuilder instanceof GeoShapeQueryBuilder) { GeoShapeQueryBuilder geoShapeQueryBuilder = (GeoShapeQueryBuilder) queryBuilder; if (geoShapeQueryBuilder.shape() == null) { throw new IllegalArgumentException("geoshape query referring to indexed shapes isn't supported as part of a role query"); } } else if (queryBuilder.getName().equals("percolate")) { // actually only if percolate query is referring to an existing document then this is problematic, // a normal percolate query does work. However we can't check that here as this query builder is inside // another module. So we don't allow the entire percolate query. I don't think users would ever use // a percolate query as role query, so this restriction shouldn't prohibit anyone from using dls. throw new IllegalArgumentException("percolate query isn't supported as part of a role query"); } else if (queryBuilder.getName().equals("has_child")) { throw new IllegalArgumentException("has_child query isn't supported as part of a role query"); } else if (queryBuilder.getName().equals("has_parent")) { throw new IllegalArgumentException("has_parent query isn't supported as part of a role query"); } else if (queryBuilder instanceof BoolQueryBuilder) { BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) queryBuilder; List clauses = new ArrayList<>(); clauses.addAll(boolQueryBuilder.filter()); clauses.addAll(boolQueryBuilder.must()); clauses.addAll(boolQueryBuilder.mustNot()); clauses.addAll(boolQueryBuilder.should()); for (QueryBuilder clause : clauses) { verifyRoleQuery(clause); } } else if (queryBuilder instanceof ConstantScoreQueryBuilder) { verifyRoleQuery(((ConstantScoreQueryBuilder) queryBuilder).innerQuery()); } else if (queryBuilder instanceof FunctionScoreQueryBuilder) { verifyRoleQuery(((FunctionScoreQueryBuilder) queryBuilder).query()); } else if (queryBuilder instanceof BoostingQueryBuilder) { verifyRoleQuery(((BoostingQueryBuilder) queryBuilder).negativeQuery()); verifyRoleQuery(((BoostingQueryBuilder) queryBuilder).positiveQuery()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy