/* * Copyright (C) 2007-2020 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.craftercms.studio.impl.v2.service.search; import java.io.IOException; import java.util.List; import org.apache.commons.collections4.CollectionUtils; import org.craftercms.search.elasticsearch.ElasticsearchWrapper; import org.craftercms.search.elasticsearch.impl.AbstractElasticsearchWrapper; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.springframework.beans.factory.annotation.Required; /** * Implementation of {@link ElasticsearchWrapper} specific for authoring indexes * @author joseross */ public class PermissionAwareSearchService extends AbstractElasticsearchWrapper { /** * The suffix to append to the site name */ protected String indexSuffix; /** * The name of the field to filter paths */ protected String pathFieldName; @Required public void setIndexSuffix(final String indexSuffix) { this.indexSuffix = indexSuffix; } @Required public void setPathFieldName(final String pathFieldName) { this.pathFieldName = pathFieldName; } /** * Perform a search operation for the given site * @param siteId the site id * @param allowedPaths the paths that should be included in the results * @param request the search request * @return the search response * @throws IOException if there is an error connecting to Elasticsearch */ public SearchResponse search(String siteId, List<String> allowedPaths, SearchRequest request) throws IOException { return search(siteId, allowedPaths, request, RequestOptions.DEFAULT); } /** * Perform a search operation for the given site * @param siteId the site id * @param allowedPaths the paths that should be included in the results * @param request the search request * @param options the request options * @return the search response * @throws IOException if there is an error connecting to Elasticsearch */ public SearchResponse search(String siteId, List<String> allowedPaths, SearchRequest request, RequestOptions options) throws IOException { request.indices(siteId + indexSuffix); //TODO: Prevent running the search without allowedPaths if(CollectionUtils.isNotEmpty(allowedPaths)) { updateFilters(request, allowedPaths); } return client.search(request, options); } protected void updateFilters(SearchRequest request, List<String> allowedPaths) { QueryBuilder query = request.source().query(); BoolQueryBuilder boolQuery; if(query instanceof BoolQueryBuilder) { boolQuery = (BoolQueryBuilder) query; } else { boolQuery = QueryBuilders.boolQuery().must(query); } //TODO: Check if allowedPaths will be regexes already allowedPaths.forEach(path -> boolQuery.filter(QueryBuilders.regexpQuery(pathFieldName, path + ".*"))); request.source().query(boolQuery); } /** * {@inheritDoc} */ @Override protected void updateIndex(final SearchRequest request) { // do nothing, this method will not be used } /** * {@inheritDoc} */ @Override public SearchResponse search(final SearchRequest request, final RequestOptions options) { // Prevent execution of requests without permission filters throw new UnsupportedOperationException(); } }