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

com.floragunn.searchguard.resolver.IndexResolverReplacer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015-2018 floragunn GmbH
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.floragunn.searchguard.resolver;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.IndicesRequest.Replaceable;
import org.elasticsearch.action.OriginalIndices;
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkItemRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkShardRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.MultiGetRequest;
import org.elasticsearch.action.get.MultiGetRequest.Item;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.main.MainRequest;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.MultiSearchRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.support.nodes.BaseNodesRequest;
import org.elasticsearch.action.support.replication.ReplicationRequest;
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
import org.elasticsearch.action.termvectors.MultiTermVectorsRequest;
import org.elasticsearch.action.termvectors.TermVectorsRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.elasticsearch.snapshots.SnapshotInfo;
import org.elasticsearch.snapshots.SnapshotUtils;
import org.elasticsearch.transport.RemoteClusterService;
import org.elasticsearch.transport.TransportRequest;

import com.floragunn.searchguard.SearchGuardPlugin;
import com.floragunn.searchguard.configuration.ClusterInfoHolder;
import com.floragunn.searchguard.configuration.ConfigurationChangeListener;
import com.floragunn.searchguard.support.SnapshotRestoreHelper;
import com.floragunn.searchguard.support.WildcardMatcher;
import com.google.common.collect.Sets;

public final class IndexResolverReplacer implements ConfigurationChangeListener {

    private static final Set NULL_SET = Sets.newHashSet((String)null);
    private final Map, Method> typeCache = Collections.synchronizedMap(new HashMap, Method>(100));
    private final Map, Method> typesCache = Collections.synchronizedMap(new HashMap, Method>(100));
    private final Logger log = LogManager.getLogger(this.getClass());
    private final IndexNameExpressionResolver resolver;
    private final ClusterService clusterService;
    private final ClusterInfoHolder clusterInfoHolder;
    private volatile boolean respectRequestIndicesOptions = false;

    public IndexResolverReplacer(IndexNameExpressionResolver resolver, ClusterService clusterService, ClusterInfoHolder clusterInfoHolder) {
        super();
        this.resolver = resolver;
        this.clusterService = clusterService;
        this.clusterInfoHolder = clusterInfoHolder;
    }

    private static final boolean isAllWithNoRemote(final String... requestedPatterns) {

        final List patterns = requestedPatterns==null?null:Arrays.asList(requestedPatterns);

        if(IndexNameExpressionResolver.isAllIndices(patterns)) {
            return true;
        }

        if(patterns.size() == 1 && patterns.contains("*")) {
            return true;
        }

        if(new HashSet(patterns).equals(NULL_SET)) {
            return true;
        }

        return false;
    }
    
    private static final boolean isLocalAll(final String... requestedPatterns) {

        final List patterns = requestedPatterns==null?null:Arrays.asList(requestedPatterns);
        
        if(IndexNameExpressionResolver.isAllIndices(patterns)) {
            return true;
        }

        if(patterns.contains("_all")) {
            return true;
        }
        
        if(new HashSet(patterns).equals(NULL_SET)) {
            return true;
        }

        return false;
    }

    private Resolved resolveIndexPatterns(final IndicesOptions indicesOptions, final Object request, final String... requestedPatterns0) {

        if(log.isTraceEnabled()) {
            log.trace("resolve requestedPatterns: "+Arrays.toString(requestedPatterns0));
        }
        
        if(isAllWithNoRemote(requestedPatterns0)) {
            if(log.isTraceEnabled()) {
                log.trace(Arrays.toString(requestedPatterns0)+" is an ALL pattern without any remote indices");
            }
            return Resolved._LOCAL_ALL;
        }
        
        Set remoteIndices;
        final List localRequestedPatterns = new ArrayList<>(Arrays.asList(requestedPatterns0));
        
        final RemoteClusterService remoteClusterService = SearchGuardPlugin.GuiceHolder.getRemoteClusterService();
        
        if(remoteClusterService.isCrossClusterSearchEnabled() && request != null && (request instanceof FieldCapabilitiesRequest || request instanceof SearchRequest)) {
            remoteIndices = new HashSet<>();
            final Map remoteClusterIndices = SearchGuardPlugin.GuiceHolder.getRemoteClusterService().groupIndices(
                    indicesOptions, requestedPatterns0, idx -> resolver.hasIndexOrAlias(idx, clusterService.state()));
            final Set remoteClusters = remoteClusterIndices.keySet().stream().filter(k->!RemoteClusterService.LOCAL_CLUSTER_GROUP_KEY.equals(k)).collect(Collectors.toSet());
            for(String remoteCluster: remoteClusters) {
                for(String remoteIndex: remoteClusterIndices.get(remoteCluster).indices()) {
                    remoteIndices.add(RemoteClusterService.buildRemoteIndexName(remoteCluster, remoteIndex));
                }
            }
            
            final Iterator iterator = localRequestedPatterns.iterator();
            while(iterator.hasNext()) {
                final String[] split = iterator.next().split(String.valueOf(RemoteClusterService.REMOTE_CLUSTER_INDEX_SEPARATOR), 2);
                if(split.length > 1 && WildcardMatcher.matchAny(split[0], remoteClusters)) {
                    iterator.remove();
                }
            }
            
            if(log.isTraceEnabled()) {
                log.trace("CCS is enabled, we found this local patterns "+localRequestedPatterns+" and this remote patterns: "+remoteIndices);
            }
            
        } else {
            remoteIndices = Collections.emptySet();
        }

        final Set matchingAliases;
        final Set matchingIndices;
        final Set matchingAllIndices;
        
       if(isLocalAll(requestedPatterns0)) {
           if(log.isTraceEnabled()) {
               log.trace(Arrays.toString(requestedPatterns0)+" is an LOCAL ALL pattern");
           }
           matchingAliases = Resolved.All_SET;
           matchingIndices = Resolved.All_SET;
           matchingAllIndices = Resolved.All_SET;
           
       } else if (!remoteIndices.isEmpty() && localRequestedPatterns.isEmpty()){
           if(log.isTraceEnabled()) {
               log.trace(Arrays.toString(requestedPatterns0)+" is an LOCAL EMPTY request");
           }
           return new Resolved.Builder().addOriginalRequested(Arrays.asList(requestedPatterns0)).addRemoteIndices(remoteIndices).build();
       } else {

           ClusterState state = clusterService.state();
    
           final SortedMap lookup = state.metaData().getAliasAndIndexLookup();
           final Set aliases = lookup.entrySet().stream().filter(e->e.getValue().isAlias()).map(e->e.getKey()).collect(Collectors.toSet());
    
           matchingAliases = new HashSet<>(localRequestedPatterns.size()*10);
           matchingIndices = new HashSet<>(localRequestedPatterns.size()*10);
           matchingAllIndices = new HashSet<>(localRequestedPatterns.size()*10);
    
           //fill matchingAliases
           for (String localRequestedPattern: localRequestedPatterns) {
               final String requestedPattern = resolver.resolveDateMathExpression(localRequestedPattern);
               final List _aliases = WildcardMatcher.getMatchAny(requestedPattern, aliases);
               matchingAliases.addAll(_aliases);
           }
    
    
           //-alias not possible
    
            {
                //final String requestedPattern = resolver.resolveDateMathExpression(requestedPatterns[i]);
                //final List _aliases = WildcardMatcher.getMatchAny(requestedPattern, aliases);
                //matchingAliases.addAll(_aliases);
    
                List _indices;
                try {
                    _indices = new ArrayList<>(Arrays.asList(resolver.concreteIndexNames(state, indicesOptions, localRequestedPatterns.toArray(new String[0]))));
                    if (log.isDebugEnabled()) {
                        log.debug("Resolved pattern {} to {}", localRequestedPatterns, _indices);
                    }
                } catch (IndexNotFoundException e1) {
                    if (log.isDebugEnabled()) {
                        log.debug("No such indices for pattern {}, use raw value", localRequestedPatterns);
                    }
    
                    _indices = new ArrayList<>(localRequestedPatterns.size());
    
                    for (String requestedPattern: localRequestedPatterns) {
                        _indices.add(resolver.resolveDateMathExpression(requestedPattern));
                    }
    
                    /*if(requestedPatterns.length == 1) {
                        _indices = Collections.singletonList(resolver.resolveDateMathExpression(requestedPatterns[0]));
                    } else {
                        log.warn("Multiple ({}) index patterns {} cannot be resolved, assume _all", requestedPatterns.length, requestedPatterns);
                        //_indices = Collections.singletonList("*");
                        _indices = Arrays.asList(requestedPatterns); //date math not handled
                    }*/
    
                }
    
                final List _aliases = WildcardMatcher.getMatchAny(localRequestedPatterns.toArray(new String[0]), aliases);
    
                matchingAllIndices.addAll(_indices);
    
               if(_aliases.isEmpty()) {
                   matchingIndices.addAll(_indices); //date math resolved?
               } else {
    
                   if(!_indices.isEmpty()) {
    
                       for(String al:_aliases) {
                           Set doubleIndices = lookup.get(al).getIndices().stream().map(a->a.getIndex().getName()).collect(Collectors.toSet());
                           _indices.removeAll(doubleIndices);
                       }
    
                       matchingIndices.addAll(_indices);
                   }
               }
           }
       }

        return new Resolved.Builder(matchingAliases, matchingIndices, matchingAllIndices, 
                null, requestedPatterns0, remoteIndices).addTypes(resolveTypes(request)).build();

    }

    @SuppressWarnings("rawtypes")
    private Set resolveTypes(final Object request) {
        // check if type security is enabled
        final Class requestClass = request.getClass();
        final Set requestTypes = new HashSet();

        if (true) {
            if (request instanceof BulkShardRequest) {
                BulkShardRequest bsr = (BulkShardRequest) request;
                for (BulkItemRequest bir : bsr.items()) {
                    requestTypes.add(bir.request().type());
                }
            } else if (request instanceof DocWriteRequest) {
                requestTypes.add(((DocWriteRequest) request).type());
            } else if (request instanceof SearchRequest) {
                requestTypes.addAll(Arrays.asList(((SearchRequest) request).types()));
            } else if (request instanceof GetRequest) {
                requestTypes.add(((GetRequest) request).type());
            } else {

                Method typeMethod = null;
                if (typeCache.containsKey(requestClass)) {
                    typeMethod = typeCache.get(requestClass);
                } else {
                    try {
                        typeMethod = requestClass.getMethod("type");
                        typeCache.put(requestClass, typeMethod);
                    } catch (NoSuchMethodException e) {
                        typeCache.put(requestClass, null);
                    } catch (SecurityException e) {
                        log.error("Cannot evaluate type() for {} due to {}", requestClass, e, e);
                    }

                }

                Method typesMethod = null;
                if (typesCache.containsKey(requestClass)) {
                    typesMethod = typesCache.get(requestClass);
                } else {
                    try {
                        typesMethod = requestClass.getMethod("types");
                        typesCache.put(requestClass, typesMethod);
                    } catch (NoSuchMethodException e) {
                        typesCache.put(requestClass, null);
                    } catch (SecurityException e) {
                        log.error("Cannot evaluate types() for {} due to {}", requestClass, e, e);
                    }

                }

                if (typeMethod != null) {
                    try {
                        String type = (String) typeMethod.invoke(request);
                        if (type != null) {
                            requestTypes.add(type);
                        }
                    } catch (Exception e) {
                        log.error("Unable to invoke type() for {} due to", requestClass, e);
                    }
                }

                if (typesMethod != null) {
                    try {
                        final String[] types = (String[]) typesMethod.invoke(request);

                        if (types != null) {
                            requestTypes.addAll(Arrays.asList(types));
                        }
                    } catch (Exception e) {
                        log.error("Unable to invoke types() for {} due to", requestClass, e);
                    }
                }
            }

        }

        if (log.isTraceEnabled()) {
            log.trace("requestTypes {} for {}", requestTypes, request.getClass());
        }

        return Collections.unmodifiableSet(requestTypes);
    }

    /*public boolean exclude(final TransportRequest request, String... exclude) {
        return getOrReplaceAllIndices(request, new IndicesProvider() {

            @Override
            public String[] provide(final String[] original, final Object request, final boolean supportsReplace) {
                if(supportsReplace) {
                    
                    final List result = new ArrayList(Arrays.asList(original));
                    
//                    if(isAll(original)) {
//                        result = new ArrayList(Collections.singletonList("*"));
//                    } else {
//                        result = new ArrayList(Arrays.asList(original));
//                    }
                    
                    
                    
                    final Set preliminary = new HashSet<>(resolveIndexPatterns(result.toArray(new String[0])).allIndices);      
                    
                    if(log.isTraceEnabled()) {
                        log.trace("resolved original {}, excludes {}",preliminary, Arrays.toString(exclude));
                    }
                    
                    WildcardMatcher.wildcardRetainInSet(preliminary, exclude);      
                    
                    if(log.isTraceEnabled()) {
                        log.trace("modified original {}",preliminary);
                    }
                    
                    result.addAll(preliminary.stream().map(a->"-"+a).collect(Collectors.toList()));

                    if(log.isTraceEnabled()) {
                        log.trace("exclude for {}: replaced {} with {}", request.getClass().getSimpleName(), Arrays.toString(original) ,result);
                    }
                    
                    return result.toArray(new String[0]);
                } else {
                    return NOOP;
                }
            }
        }, false);
    }*/

    //dnfof
    public boolean replace(final TransportRequest request, boolean retainMode, String... replacements) {
        return getOrReplaceAllIndices(request, new IndicesProvider() {

            @Override
            public String[] provide(String[] original, Object request, boolean supportsReplace) {
                if(supportsReplace) {

                    if(retainMode && !isAllWithNoRemote(original)) {
                        final Resolved resolved = resolveRequest(request);
                        final List retained = WildcardMatcher.getMatchAny(resolved.getAllIndices(), replacements);
                        retained.addAll(resolved.getRemoteIndices());
                        return retained.toArray(new String[0]);
                    }
                    return replacements;
                } else {
                    return NOOP;
                }
            }
        }, false);
    }

    public Resolved resolveRequest(final Object request) {
        if(log.isDebugEnabled()) {
            log.debug("Resolve aliases, indices and types from {}", request.getClass().getSimpleName());
        }

        final Resolved.Builder resolvedBuilder = new Resolved.Builder();
        final AtomicBoolean isIndicesRequest = new AtomicBoolean();
        getOrReplaceAllIndices(request, new IndicesProvider() {

            @Override
            public String[] provide(String[] original, Object localRequest, boolean supportsReplace) {
                final IndicesOptions indicesOptions = indicesOptionsFrom(localRequest);
                final Resolved iResolved = resolveIndexPatterns(indicesOptions, localRequest, original);
                resolvedBuilder.add(iResolved);
                isIndicesRequest.set(true);

                if(log.isTraceEnabled()) {
                    log.trace("Resolved patterns {} for {} ({}) to {}", original, localRequest.getClass().getSimpleName(), request.getClass().getSimpleName(), iResolved);
                }

                return IndicesProvider.NOOP;
            }
        }, false);

        if(!isIndicesRequest.get()) {
            //not an indices request
            return Resolved._LOCAL_ALL;
        }
        
        if(log.isTraceEnabled()) {
            log.trace("Finally resolved for {}: {}", request.getClass().getSimpleName(), resolvedBuilder.build());
        }

        return resolvedBuilder.build();
    }

    public final static class Resolved implements Serializable, Writeable {

        /**
         *
         */
        private static final Set All_SET = Collections.singleton("*");
        private static final long serialVersionUID = 1L;
        public final static Resolved _LOCAL_ALL = new Resolved(All_SET, All_SET, All_SET, All_SET, Collections.emptySet(), Collections.emptySet());
        private final Set aliases;
        private final Set indices;
        private final Set allIndices;
        private final Set types;
        
        private final Set originalRequested;
        private final Set remoteIndices;

        private Resolved(final Set aliases, final Set indices, final Set allIndices, 
                final Set types, final Set originalRequested, final Set remoteIndices) {
            super();
            this.aliases = aliases;
            this.indices = indices;
            this.allIndices = allIndices;
            this.types = types;
            this.originalRequested = originalRequested;
            this.remoteIndices = remoteIndices;

            if(!aliases.isEmpty() || !indices.isEmpty() || !allIndices.isEmpty()) {
                if(types.isEmpty()) {
                    throw new ElasticsearchException("Empty types for nonempty indices or aliases");
                }
            }
        }

        public boolean isLocalAll() {
            if(IndexResolverReplacer.isLocalAll(originalRequested==null?null:originalRequested.toArray(new String[0]))) {
                return true;
            }
            
            return aliases.contains("*") && indices.contains("*") && allIndices.contains("*") && types.contains("*");
        }

        public Set getAliases() {
            return Collections.unmodifiableSet(aliases);
        }

        public Set getIndices() {
            return Collections.unmodifiableSet(indices);
        }

        public Set getAllIndices() {
            return Collections.unmodifiableSet(allIndices);
        }
        
        public Set getTypes() {
            return Collections.unmodifiableSet(types);
        }

        public Set getOriginalRequested() {
            return Collections.unmodifiableSet(originalRequested);
        }
        
        public Set getRemoteIndices() {
            return Collections.unmodifiableSet(remoteIndices);
        }

        @Override
        public String toString() {
            return "Resolved [aliases=" + aliases + ", indices=" + indices + ", allIndices=" + allIndices + ", types=" + types
                    + ", originalRequested=" + originalRequested + ", remoteIndices=" + remoteIndices + "]";
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((aliases == null) ? 0 : aliases.hashCode());
            result = prime * result + ((allIndices == null) ? 0 : allIndices.hashCode());
            result = prime * result + ((indices == null) ? 0 : indices.hashCode());
            result = prime * result + ((originalRequested == null) ? 0 : originalRequested.hashCode());
            result = prime * result + ((remoteIndices == null) ? 0 : remoteIndices.hashCode());
            result = prime * result + ((types == null) ? 0 : types.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Resolved other = (Resolved) obj;
            if (aliases == null) {
                if (other.aliases != null)
                    return false;
            } else if (!aliases.equals(other.aliases))
                return false;
            if (allIndices == null) {
                if (other.allIndices != null)
                    return false;
            } else if (!allIndices.equals(other.allIndices))
                return false;
            if (indices == null) {
                if (other.indices != null)
                    return false;
            } else if (!indices.equals(other.indices))
                return false;
            if (originalRequested == null) {
                if (other.originalRequested != null)
                    return false;
            } else if (!originalRequested.equals(other.originalRequested))
                return false;
            if (remoteIndices == null) {
                if (other.remoteIndices != null)
                    return false;
            } else if (!remoteIndices.equals(other.remoteIndices))
                return false;
            if (types == null) {
                if (other.types != null)
                    return false;
            } else if (!types.equals(other.types))
                return false;
            return true;
        }



        private static class Builder {

            private final Set aliases = new HashSet();
            private final Set indices = new HashSet();
            private final Set allIndices = new HashSet();
            private final Set types = new HashSet();
            private final Set originalRequested = new HashSet();
            private final Set remoteIndices = new HashSet();
            
            public Builder() {
                this(null, null, null, null, null, null);
            }

            public Builder(Collection aliases, Collection indices, Collection allIndices, 
                    Collection types, String[] originalRequested, Collection remoteIndices) {

                if(aliases != null) {
                    this.aliases.addAll(aliases);
                }

                if(indices != null) {
                    this.indices.addAll(indices);
                }

                if(allIndices != null) {
                    this.allIndices.addAll(allIndices);
                }

                if(types != null) {
                    this.types.addAll(types);
                }
                
                if(originalRequested != null) {
                    this.originalRequested.addAll(Arrays.asList(originalRequested));
                }
                
                if(remoteIndices != null) {
                    this.remoteIndices.addAll(remoteIndices);
                }
            }
            
            public Builder addTypes(Collection types) {
                if(types != null && types.size() > 0) {
                    if(this.types.contains("*")) {
                        this.types.remove("*");
                    }
                    this.types.addAll(types);
                }
                return this;
            }

            public Builder add(Resolved r) {

                this.aliases.addAll(r.aliases);
                this.indices.addAll(r.indices);
                this.allIndices.addAll(r.allIndices);
                this.originalRequested.addAll(r.originalRequested);
                this.remoteIndices.addAll(r.remoteIndices);
                addTypes(r.types);
                return this;
            }
            
            public Builder addOriginalRequested(List originalRequested) {
                if(originalRequested != null) {
                    this.originalRequested.addAll(originalRequested);
                }
                return this;
            }
            
            public Builder addRemoteIndices(Set remoteIndices) {
                if(remoteIndices != null) {
                    this.remoteIndices.addAll(remoteIndices);
                }
                return this;
            }

            public Resolved build() {
                if(types.isEmpty()) {
                    types.add("*");
                }

                return new Resolved(new HashSet(aliases), new HashSet(indices), new HashSet(allIndices), 
                        new HashSet(types), new HashSet(originalRequested), new HashSet(remoteIndices));
            }
        }

        public Resolved(final StreamInput in) throws IOException {
            aliases = new HashSet(in.readList(StreamInput::readString));
            indices = new HashSet(in.readList(StreamInput::readString));
            allIndices = new HashSet(in.readList(StreamInput::readString));
            types = new HashSet(in.readList(StreamInput::readString));
            originalRequested = new HashSet(in.readList(StreamInput::readString));
            remoteIndices = new HashSet(in.readList(StreamInput::readString));
        }


        @Override
        public void writeTo(StreamOutput out) throws IOException {
            out.writeStringCollection(new ArrayList<>(aliases));
            out.writeStringCollection(new ArrayList<>(indices));
            out.writeStringCollection(new ArrayList<>(allIndices));
            out.writeStringCollection(new ArrayList<>(types));
            out.writeStringCollection(new ArrayList<>(originalRequested));
            out.writeStringCollection(new ArrayList<>(remoteIndices));
        }
    }

    private List renamedIndices(final RestoreSnapshotRequest request, final List filteredIndices) {
        try {
            final List renamedIndices = new ArrayList<>();
            for (final String index : filteredIndices) {
                String renamedIndex = index;
                if (request.renameReplacement() != null && request.renamePattern() != null) {
                    renamedIndex = index.replaceAll(request.renamePattern(), request.renameReplacement());
                }
                renamedIndices.add(renamedIndex);
            }
            return renamedIndices;
        } catch (PatternSyntaxException e) {
            log.error("Unable to parse the regular expression denoted in 'rename_pattern'. Please correct the pattern an try again.");
            throw e;
        }
    }


    //--

    @FunctionalInterface
    public interface IndicesProvider {
        public static final String[] NOOP = new String[0];
        String[] provide(String[] original, Object request, boolean supportsReplace);
    }

    private boolean checkIndices(Object request, String[] indices, boolean needsToBeSizeOne, boolean allowEmpty) {

        if(indices == IndicesProvider.NOOP) {
            return false;
        }

        if(!allowEmpty && (indices == null || indices.length == 0)) {
            if(log.isTraceEnabled() && request != null) {
                log.trace("Null or empty indices for "+request.getClass().getName());
            }
            return false;
        }

        if(!allowEmpty && needsToBeSizeOne && indices.length != 1) {
            if(log.isTraceEnabled() && request != null) {
                log.trace("To much indices for "+request.getClass().getName());
            }
            return false;
        }

        for (int i = 0; i < indices.length; i++) {
            final String index = indices[i];
            if(index == null || index.isEmpty()) {
                //not allowed
                if(log.isTraceEnabled() && request != null) {
                    log.trace("At least one null or empty index for "+request.getClass().getName());
                }
                return false;
            }
        }

        return true;
    }

    /**
     * new
     * @param request
     * @param newIndices
     * @return
     */
    @SuppressWarnings("rawtypes")
    private boolean getOrReplaceAllIndices(final Object request, final IndicesProvider provider, boolean allowEmptyIndices) {

        if(log.isTraceEnabled()) {
            log.trace("getOrReplaceAllIndices() for "+request.getClass());
        }

        boolean result = true;

        if (request instanceof BulkRequest) {

            for (DocWriteRequest ar : ((BulkRequest) request).requests()) {
                result = getOrReplaceAllIndices(ar, provider, false) && result;
            }

        } else if (request instanceof MultiGetRequest) {

            for (ListIterator it = ((MultiGetRequest) request).getItems().listIterator(); it.hasNext();){
                Item item = it.next();
                result = getOrReplaceAllIndices(item, provider, false) && result;
                /*if(item.index() == null || item.indices() == null || item.indices().length == 0) {
                    it.remove();
                }*/
            }

        } else if (request instanceof MultiSearchRequest) {

            for (ListIterator it = ((MultiSearchRequest) request).requests().listIterator(); it.hasNext();) {
                SearchRequest ar = it.next();
                result = getOrReplaceAllIndices(ar, provider, false) && result;
                /*if(ar.indices() == null || ar.indices().length == 0) {
                    it.remove();
                }*/
            }

        } else if (request instanceof MultiTermVectorsRequest) {

            for (ActionRequest ar : (Iterable) () -> ((MultiTermVectorsRequest) request).iterator()) {
                result = getOrReplaceAllIndices(ar, provider, false) && result;
            }

        } else if(request instanceof PutMappingRequest) {
            PutMappingRequest pmr = (PutMappingRequest) request;
            Index concreteIndex = pmr.getConcreteIndex();
            if(concreteIndex != null && (pmr.indices() == null || pmr.indices().length == 0)) {
                String[] newIndices = provider.provide(new String[]{concreteIndex.getName()}, request, true);
                if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                    return false;
                }
                
                ((PutMappingRequest) request).indices(newIndices);
                ((PutMappingRequest) request).setConcreteIndex(null);
            } else {
                String[] newIndices = provider.provide(((PutMappingRequest) request).indices(), request, true);
                if(checkIndices(request, newIndices, false, allowEmptyIndices) == false) {
                    return false;
                }
                ((PutMappingRequest) request).indices(newIndices);
            }
        } else if(request instanceof RestoreSnapshotRequest) {
            
                if(clusterInfoHolder.isLocalNodeElectedMaster() == Boolean.FALSE) {
                    return true;
                }      

                final RestoreSnapshotRequest restoreRequest = (RestoreSnapshotRequest) request;
                final SnapshotInfo snapshotInfo = SnapshotRestoreHelper.getSnapshotInfo(restoreRequest);

                if (snapshotInfo == null) {
                    log.warn("snapshot repository '" + restoreRequest.repository() + "', snapshot '" + restoreRequest.snapshot() + "' not found");
                    provider.provide(new String[]{"*"}, request, false);
                } else {
                    final List requestedResolvedIndices = SnapshotUtils.filterIndices(snapshotInfo.indices(), restoreRequest.indices(), restoreRequest.indicesOptions());
                    final List renamedTargetIndices = renamedIndices(restoreRequest, requestedResolvedIndices);
                    //final Set indices = new HashSet<>(requestedResolvedIndices);
                    //indices.addAll(renamedTargetIndices);
                    if(log.isDebugEnabled()) {
                        log.debug("snapshot: {} contains this indices: {}", snapshotInfo.snapshotId().getName(), renamedTargetIndices);
                    }
                    provider.provide(renamedTargetIndices.toArray(new String[0]), request, false);
                }

        } else if (request instanceof IndicesAliasesRequest) {
            for(AliasActions ar: ((IndicesAliasesRequest) request).getAliasActions()) {
                result = getOrReplaceAllIndices(ar, provider, false) && result;
            }
        } else if (request instanceof DeleteRequest) {
            String[] newIndices = provider.provide(((DeleteRequest) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((DeleteRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof UpdateRequest) {
            String[] newIndices = provider.provide(((UpdateRequest) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((UpdateRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof SingleShardRequest) {
            final SingleShardRequest gr = (SingleShardRequest) request;
            final String[] indices = gr.indices();
            final String index = gr.index();

            final List indicesL = new ArrayList();

            if (index != null) {
                indicesL.add(index);
            }

            if (indices != null && indices.length > 0) {
                indicesL.addAll(Arrays.asList(indices));
            }

            String[] newIndices = provider.provide(indicesL.toArray(new String[0]), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((SingleShardRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof IndexRequest) {
            String[] newIndices = provider.provide(((IndexRequest) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((IndexRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof Replaceable) {
            String[] newIndices = provider.provide(((Replaceable) request).indices(), request, true);
            if(checkIndices(request, newIndices, false, allowEmptyIndices) == false) {
                return false;
            }
            ((Replaceable) request).indices(newIndices);
        } else if (request instanceof BulkShardRequest) {
            provider.provide(((ReplicationRequest) request).indices(), request, false);
            //replace not supported?
        } else if (request instanceof ReplicationRequest) {
            String[] newIndices = provider.provide(((ReplicationRequest) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((ReplicationRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof MultiGetRequest.Item) {
            String[] newIndices = provider.provide(((MultiGetRequest.Item) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((MultiGetRequest.Item) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof CreateIndexRequest) {
            String[] newIndices = provider.provide(((CreateIndexRequest) request).indices(), request, true);
            if(checkIndices(request, newIndices, true, allowEmptyIndices) == false) {
                return false;
            }
            ((CreateIndexRequest) request).index(newIndices.length!=1?null:newIndices[0]);
        } else if (request instanceof ReindexRequest) {
            result = getOrReplaceAllIndices(((ReindexRequest) request).getDestination(), provider, false) && result;
            result = getOrReplaceAllIndices(((ReindexRequest) request).getSearchRequest(), provider, false) && result;
        } else if (request instanceof BaseNodesRequest) {
            //do nothing
        } else if (request instanceof MainRequest) {
            //do nothing
        } else if (request instanceof ClearScrollRequest) {
            //do nothing
        } else if (request instanceof SearchScrollRequest) {
            //do nothing
        } else {
            if(log.isDebugEnabled()) {
                log.debug(request.getClass() + " not supported (It is likely not a indices related request)");
            }
            result = false;
        }

        return result;
    }
    
    private IndicesOptions indicesOptionsFrom(Object localRequest) {
        
        if(!respectRequestIndicesOptions) {
            return IndicesOptions.fromOptions(false, true, true, false);
        }
        
        if (IndicesRequest.class.isInstance(localRequest)) {
            return ((IndicesRequest) localRequest).indicesOptions();
        }
        else if (RestoreSnapshotRequest.class.isInstance(localRequest)) {
            return ((RestoreSnapshotRequest) localRequest).indicesOptions();
        }
        else {
            return IndicesOptions.fromOptions(false, true, true, false);
        }
    }

    @Override
    public void onChange(Settings dynamicSgConfig) {
        respectRequestIndicesOptions = dynamicSgConfig.getAsBoolean("searchguard.dynamic.respect_request_indices_options", false);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy