/*
* Copyright 2015 floragunn UG (haftungsbeschränkt)
*
* 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.configuration;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.IndicesRequest;
import org.elasticsearch.action.RealtimeRequest;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.bulk.BulkAction;
import org.elasticsearch.action.get.MultiGetAction;
import org.elasticsearch.action.percolate.MultiPercolateAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.action.termvectors.MultiTermVectorsAction;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.collect.ImmutableOpenMap;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.transport.TransportRequest;
import com.floragunn.searchguard.action.configupdate.TransportConfigUpdateAction;
import com.floragunn.searchguard.auditlog.AuditLog;
import com.floragunn.searchguard.support.Base64Helper;
import com.floragunn.searchguard.support.ConfigConstants;
import com.floragunn.searchguard.support.WildcardMatcher;
import com.floragunn.searchguard.user.User;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.common.collect.UnmodifiableIterator;
public class PrivilegesEvaluator implements ConfigChangeListener {
private static final Set<String> NULL_SET = Sets.newHashSet((String)null);
private final Set<String> DLSFLS = ImmutableSet.of("_dls_", "_fls_");
protected final ESLogger log = Loggers.getLogger(this.getClass());
private final ClusterService clusterService;
private volatile Settings rolesMapping;
private volatile Settings roles;
private volatile Settings config;
private final ActionGroupHolder ah;
private final IndexNameExpressionResolver resolver;
private final Map<Class<?>, Method> typeCache = Collections.synchronizedMap(new HashMap<Class<?>, Method>(100));
private final Map<Class<?>, Method> typesCache = Collections.synchronizedMap(new HashMap<Class<?>, Method>(100));
private final String[] deniedActionPatterns;
private final AuditLog auditLog;
private final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
private final String searchguardIndex;
private PrivilegesInterceptor privilegesInterceptor;
@Inject
public PrivilegesEvaluator(final ClusterService clusterService, final TransportConfigUpdateAction tcua, final ActionGroupHolder ah,
final IndexNameExpressionResolver resolver, final AuditLog auditLog, final Settings settings,
final PrivilegesInterceptor privilegesInterceptor) {
super();
tcua.addConfigChangeListener(ConfigurationService.CONFIGNAME_ROLES_MAPPING, this);
tcua.addConfigChangeListener(ConfigurationService.CONFIGNAME_ROLES, this);
tcua.addConfigChangeListener(ConfigurationService.CONFIGNAME_CONFIG, this);
this.clusterService = clusterService;
this.ah = ah;
this.resolver = resolver;
this.auditLog = auditLog;
this.searchguardIndex = settings.get(ConfigConstants.SG_CONFIG_INDEX, ConfigConstants.SG_DEFAULT_CONFIG_INDEX);
this.privilegesInterceptor = privilegesInterceptor;
/*
indices:admin/template/delete
indices:admin/template/get
indices:admin/template/put
indices:admin/aliases
indices:admin/aliases/exists
indices:admin/aliases/get
indices:admin/analyze
indices:admin/cache/clear
-> indices:admin/close
indices:admin/create
-> indices:admin/delete
indices:admin/get
indices:admin/exists
indices:admin/flush
indices:admin/mapping/put
indices:admin/mappings/fields/get
indices:admin/mappings/get
indices:admin/open
indices:admin/optimize
indices:admin/refresh
indices:admin/settings/update
indices:admin/shards/search_shards
indices:admin/types/exists
indices:admin/upgrade
indices:admin/validate/query
indices:admin/warmers/delete
indices:admin/warmers/get
indices:admin/warmers/put
*/
final List<String> deniedActionPatternsList = new ArrayList<String>();
deniedActionPatternsList.add("indices:data/write*");
deniedActionPatternsList.add("indices:admin/close");
deniedActionPatternsList.add("indices:admin/delete");
//deniedActionPatternsList.add("indices:admin/settings/update");
//deniedActionPatternsList.add("indices:admin/upgrade");
deniedActionPatterns = deniedActionPatternsList.toArray(new String[0]);
}
@Override
public void onChange(final String event, final Settings settings) {
switch (event) {
case "roles":
roles = settings;
break;
case "rolesmapping":
rolesMapping = settings;
break;
case "config":
config = settings;
break;
}
}
@Override
public boolean isInitialized() {
return rolesMapping != null && roles != null;
}
@Override
public void validate(final String event, final Settings settings) throws ElasticsearchSecurityException {
}
public static class IndexType {
private String index;
private String type;
public IndexType(String index, String type) {
super();
this.index = index;
this.type = type.equals("_all")? "*": type;
}
public String getCombinedString() {
return index+"#"+type;
}
public String getIndex() {
return index;
}
public String getType() {
return type;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((index == null) ? 0 : index.hashCode());
result = prime * result + ((type == null) ? 0 : type.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;
IndexType other = (IndexType) obj;
if (index == null) {
if (other.index != null)
return false;
} else if (!index.equals(other.index))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
}
@Override
public String toString() {
return "IndexType [index=" + index + ", type=" + type + "]";
}
}
public boolean evaluate(final User user, final String action, final ActionRequest<?> request) {
if(action.startsWith("cluster:admin/snapshot/restore")) {
auditLog.logMissingPrivileges(action, request);
log.warn(action + " is not allowed for a regular user");
return false;
}
final TransportAddress caller = Objects.requireNonNull((TransportAddress) request.getFromContext(ConfigConstants.SG_REMOTE_ADDRESS));
if (log.isDebugEnabled()) {
log.debug("evaluate permissions for {}", user);
log.debug("requested {} from {}", action, caller);
}
final ClusterState clusterState = clusterService.state();
final MetaData metaData = clusterState.metaData();
final Tuple<Set<String>, Set<String>> requestedResolvedAliasesIndicesTypes = resolve(user, action, request, metaData);
final Set<String> requestedResolvedIndices = Collections.unmodifiableSet(requestedResolvedAliasesIndicesTypes.v1());
final Set<IndexType> requestedResolvedIndexTypes;
{
final Set<IndexType> requestedResolvedIndexTypes0 = new HashSet<IndexType>(requestedResolvedAliasesIndicesTypes.v1().size() * requestedResolvedAliasesIndicesTypes.v2().size());
for(String index: requestedResolvedAliasesIndicesTypes.v1()) {
for(String type: requestedResolvedAliasesIndicesTypes.v2()) {
requestedResolvedIndexTypes0.add(new IndexType(index, type));
}
}
requestedResolvedIndexTypes = Collections.unmodifiableSet(requestedResolvedIndexTypes0);
}
if (log.isDebugEnabled()) {
log.debug("requested resolved indextypes: {}", requestedResolvedIndexTypes);
}
if (requestedResolvedIndices.contains(searchguardIndex)
&& WildcardMatcher.matchAny(deniedActionPatterns, action)) {
auditLog.logSgIndexAttempt(request, action);
log.warn(action + " for '{}' index is not allowed for a regular user", searchguardIndex);
return false;
}
if (requestedResolvedIndices.contains("_all")
&& WildcardMatcher.matchAny(deniedActionPatterns, action)) {
auditLog.logSgIndexAttempt(request, action);
log.warn(action + " for '_all' indices is not allowed for a regular user");
return false;
}
if(requestedResolvedIndices.contains(searchguardIndex) || requestedResolvedIndices.contains("_all")) {
if(request instanceof SearchRequest) {
((SearchRequest)request).requestCache(Boolean.FALSE);
if(log.isDebugEnabled()) {
log.debug("Disable search request cache for this request");
}
}
if(request instanceof RealtimeRequest) {
((RealtimeRequest) request).realtime(Boolean.FALSE);
if(log.isDebugEnabled()) {
log.debug("Disable realtime for this request");
}
}
}
final Set<String> sgRoles = mapSgRoles(user, caller);
if (log.isDebugEnabled()) {
log.debug("mapped roles for {}: {}", user.getName(), sgRoles);
}
if(privilegesInterceptor.getClass() != PrivilegesInterceptor.class) {
final boolean denyRequest = privilegesInterceptor.replaceKibanaIndex(request, action, user, config, requestedResolvedIndices, mapTenants(user, caller));
if (denyRequest) {
auditLog.logMissingPrivileges(action, request);
return false;
}
}
boolean allowAction = false;
final Map<String,Set<String>> dlsQueries = new HashMap<String, Set<String>>();
final Map<String,Set<String>> flsFields = new HashMap<String, Set<String>>();
final Set<IndexType> leftovers = new HashSet<PrivilegesEvaluator.IndexType>();
for (final Iterator<String> iterator = sgRoles.iterator(); iterator.hasNext();) {
final String sgRole = (String) iterator.next();
final Settings sgRoleSettings = roles.getByPrefix(sgRole);
if (sgRoleSettings.names().isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("sg_role {} is empty", sgRole);
}
continue;
}
if (log.isDebugEnabled()) {
log.debug("---------- evaluate sg_role: {}", sgRole);
}
final boolean compositeEnabled = config.getAsBoolean("searchguard.dynamic.composite_enabled", false);
if (action.startsWith("cluster:") || action.startsWith("indices:admin/template/delete")
|| action.startsWith("indices:admin/template/get") || action.startsWith("indices:admin/template/put")
|| action.startsWith("indices:data/read/scroll")
//M*
|| (compositeEnabled && action.equals(BulkAction.NAME))
|| (compositeEnabled && action.equals(IndicesAliasesAction.NAME))
|| (compositeEnabled && action.equals(MultiGetAction.NAME))
|| (compositeEnabled && action.equals(MultiPercolateAction.NAME))
|| (compositeEnabled && action.equals(MultiSearchAction.NAME))
|| (compositeEnabled && action.equals(MultiTermVectorsAction.NAME))
|| (compositeEnabled && action.equals("indices:data/read/coordinate-msearch"))
//|| (compositeEnabled && action.startsWith(MultiPercolateAction.NAME))
) {
final Set<String> resolvedActions = resolveActions(sgRoleSettings.getAsArray(".cluster", new String[0]));
if (log.isDebugEnabled()) {
log.debug(" resolved cluster actions:{}", resolvedActions);
}
if (WildcardMatcher.matchAny(resolvedActions.toArray(new String[0]), action)) {
if (log.isDebugEnabled()) {
log.debug(" found a match for '{}' and {}, skip other roles", sgRole, action);
}
return true;
} else {
//check other roles #108
if (log.isDebugEnabled()) {
log.debug(" not match found a match for '{}' and {}, check next role", sgRole, action);
}
continue;
}
}
final Map<String, Settings> permittedAliasesIndices0 = sgRoleSettings.getGroups(".indices");
final Map<String, Settings> permittedAliasesIndices = new HashMap<String, Settings>(permittedAliasesIndices0.size());
for (String origKey : permittedAliasesIndices0.keySet()) {
permittedAliasesIndices.put(origKey.replace("${user.name}", user.getName()).replace("${user_name}", user.getName()),
permittedAliasesIndices0.get(origKey));
}
/*
sg_role_starfleet:
indices:
sf: #<--- is an alias or cindex, can contain wildcards, will be resolved to concrete indices
# if this contain wildcards we do a wildcard based check
# if contains no wildcards we resolve this to concrete indices an do a exact check
#
ships: <-- is a type, can contain wildcards
- READ
public:
- 'indices:*'
students:
- READ
alumni:
- READ
'admin*':
- READ
'pub*':
'*':
- READ
*/
final ListMultimap<String, String> resolvedRoleIndices = Multimaps.synchronizedListMultimap(ArrayListMultimap
.<String, String> create());
final Set<IndexType> _requestedResolvedIndexTypes = new HashSet<IndexType>(requestedResolvedIndexTypes);
//iterate over all beneath indices:
permittedAliasesIndices:
for (final String permittedAliasesIndex : permittedAliasesIndices.keySet()) {
//final Map<String, Settings> permittedTypes = sgRoleSettings.getGroups(".indices."+permittedAliasesIndex);
//System.out.println(permittedTypes);
if (WildcardMatcher.containsWildcard(permittedAliasesIndex)) {
if (log.isDebugEnabled()) {
log.debug(" Try wildcard match for {}", permittedAliasesIndex);
}
handleIndicesWithWildcard(action, permittedAliasesIndex, permittedAliasesIndices, requestedResolvedIndexTypes, _requestedResolvedIndexTypes, requestedResolvedIndices);
} else {
if (log.isDebugEnabled()) {
log.debug(" Resolve and match {}", permittedAliasesIndex);
}
handleIndicesWithoutWildcard(action, permittedAliasesIndex, permittedAliasesIndices, requestedResolvedIndexTypes, _requestedResolvedIndexTypes);
}
if (log.isDebugEnabled()) {
log.debug("For index {} remaining requested indextype: {}", permittedAliasesIndex, _requestedResolvedIndexTypes);
}
if (_requestedResolvedIndexTypes.isEmpty()) {
int filteredAliasCount = 0;
//check filtered aliases
for(String requestAliasOrIndex: requestedResolvedIndices) {
//System.out.println(clusterState.metaData().getAliasAndIndexLookup().get(requestAliasOrIndex));
IndexMetaData indexMetaData = clusterState.metaData().getIndices().get(requestAliasOrIndex);
if(indexMetaData == null) {
log.warn("{} does not exist in cluster metadata", requestAliasOrIndex);
continue;
}
ImmutableOpenMap<String, AliasMetaData> aliases = indexMetaData.getAliases();
log.debug("Aliases for {}: {}", requestAliasOrIndex, aliases);
if(aliases != null && aliases.size() > 0) {
UnmodifiableIterator<String> it = aliases.keysIt();
while(it.hasNext()) {
String a = it.next();
AliasMetaData aliasMetaData = aliases.get(a);
if(aliasMetaData != null && aliasMetaData.filteringRequired()) {
filteredAliasCount++;
log.debug(a+" is a filtered alias "+aliasMetaData.getFilter());
} else {
log.debug(a+" is not an alias or does not have a filter");
}
}
}
}
if(filteredAliasCount > 1) {
//TODO add queries as dls queries (works only if dls module is installed)
log.warn("More than one ({}) filtered alias found for same index ({}). This is currently not supported", filteredAliasCount, permittedAliasesIndex);
continue permittedAliasesIndices;
}
if (log.isDebugEnabled()) {
log.debug("found a match for '{}.{}', evaluate other roles", sgRole, permittedAliasesIndex);
}
resolvedRoleIndices.put(sgRole, permittedAliasesIndex);
}
}// end loop permittedAliasesIndices
if (!resolvedRoleIndices.isEmpty()) {
for(String resolvedRole: resolvedRoleIndices.keySet()) {
for(String indexPattern: resolvedRoleIndices.get(resolvedRole)) {
String dls = roles.get(resolvedRole+".indices."+indexPattern+"._dls_");
final String[] fls = roles.getAsArray(resolvedRole+".indices."+indexPattern+"._fls_");
//only when dls and fls != null
String[] concreteIndices = new String[0];
if((dls != null && dls.length() > 0) || (fls != null && fls.length > 0)) {
concreteIndices = resolver.concreteIndices(clusterService.state(), DEFAULT_INDICES_OPTIONS/*??*/,indexPattern);
}
if(dls != null && dls.length() > 0) {
//TODO use UserPropertyReplacer, make it registerable for ldap user
dls = dls.replace("${user.name}", user.getName()).replace("${user_name}", user.getName());
if(dlsQueries.containsKey(indexPattern)) {
dlsQueries.get(indexPattern).add(dls);
} else {
dlsQueries.put(indexPattern, new HashSet<String>());
dlsQueries.get(indexPattern).add(dls);
}
for (int i = 0; i < concreteIndices.length; i++) {
final String ci = concreteIndices[i];
if(dlsQueries.containsKey(ci)) {
dlsQueries.get(ci).add(dls);
} else {
dlsQueries.put(ci, new HashSet<String>());
dlsQueries.get(ci).add(dls);
}
}
if (log.isDebugEnabled()) {
log.debug("dls query {} for {}", dls, Arrays.toString(concreteIndices));
}
}
if(fls != null && fls.length > 0) {
if(flsFields.containsKey(indexPattern)) {
flsFields.get(indexPattern).addAll(Sets.newHashSet(fls));
} else {
flsFields.put(indexPattern, new HashSet<String>());
flsFields.get(indexPattern).addAll(Sets.newHashSet(fls));
}
for (int i = 0; i < concreteIndices.length; i++) {
final String ci = concreteIndices[i];
if(flsFields.containsKey(ci)) {
flsFields.get(ci).addAll(Sets.newHashSet(fls));
} else {
flsFields.put(ci, new HashSet<String>());
flsFields.get(ci).addAll(Sets.newHashSet(fls));
}
}
if (log.isDebugEnabled()) {
log.debug("fls fields {} for {}", Sets.newHashSet(fls), Arrays.toString(concreteIndices));
}
}
}
}
allowAction = true;
}
leftovers.addAll(_requestedResolvedIndexTypes);
} // end sg role loop
if (!allowAction && log.isInfoEnabled()) {
log.info("No perm match for {} {} [Action [{}]] [RolesChecked {}]", user, requestedResolvedIndexTypes, action, sgRoles);
}
if(!dlsQueries.isEmpty()) {
request.putHeader(ConfigConstants.SG_DLS_QUERY, Base64Helper.serializeObject((Serializable) dlsQueries));
}
if(!flsFields.isEmpty()) {
request.putHeader(ConfigConstants.SG_FLS_FIELDS, Base64Helper.serializeObject((Serializable) flsFields));
}
if(!allowAction && privilegesInterceptor.getClass() != PrivilegesInterceptor.class) {
return privilegesInterceptor.replaceAllowedIndices(request, action, user, config, leftovers);
}
return allowAction;
}
//---- end evaluate()
public Set<String> mapSgRoles(final User user, final TransportAddress caller) {
if(user == null) {
return Collections.emptySet();
}
final Set<String> sgRoles = new TreeSet<String>();
for (final String roleMap : rolesMapping.names()) {
final Settings roleMapSettings = rolesMapping.getByPrefix(roleMap);
if (WildcardMatcher.allPatternsMatched(roleMapSettings.getAsArray(".and_backendroles"), user.getRoles().toArray(new String[0]))) {
sgRoles.add(roleMap);
continue;
}
if (WildcardMatcher.matchAny(roleMapSettings.getAsArray(".backendroles"), user.getRoles().toArray(new String[0]))) {
sgRoles.add(roleMap);
continue;
}
if (WildcardMatcher.matchAny(roleMapSettings.getAsArray(".users"), user.getName())) {
sgRoles.add(roleMap);
continue;
}
if (caller != null && WildcardMatcher.matchAny(roleMapSettings.getAsArray(".hosts"), caller.getAddress())) {
sgRoles.add(roleMap);
continue;
}
if (caller != null && WildcardMatcher.matchAny(roleMapSettings.getAsArray(".hosts"), caller.getHost())) {
sgRoles.add(roleMap);
continue;
}
}
return Collections.unmodifiableSet(sgRoles);
}
public Map<String, Boolean> mapTenants(final User user, final TransportAddress caller) {
if(user == null) {
return Collections.emptyMap();
}
final Map<String, Boolean> result = new HashMap<String, Boolean>();
result.put(user.getName(), true);
for(String sgRole: mapSgRoles(user, caller)) {
Settings tenants = roles.getByPrefix(sgRole+".tenants.");
if(tenants != null) {
for(String tenant: tenants.names()) {
if(tenant.equals(user.getName())) {
continue;
}
if("RW".equalsIgnoreCase(tenants.get(tenant, "RO"))) {
result.put(tenant, true);
} else {
if(!result.containsKey(tenant)) { //RW outperforms RO
result.put(tenant, false);
}
}
}
}
}
return Collections.unmodifiableMap(result);
}
private void handleIndicesWithWildcard(final String action, final String permittedAliasesIndex,
final Map<String, Settings> permittedAliasesIndices, final Set<IndexType> requestedResolvedIndexTypes, final Set<IndexType> _requestedResolvedIndexTypes, final Set<String> requestedResolvedIndices0) {
List<String> wi = null;
if (!(wi = WildcardMatcher.getMatchAny(permittedAliasesIndex, requestedResolvedIndices0.toArray(new String[0]))).isEmpty()) {
if (log.isDebugEnabled()) {
log.debug(" Wildcard match for {}: {}", permittedAliasesIndex, wi);
}
final Set<String> permittedTypes = new HashSet<String>(permittedAliasesIndices.get(permittedAliasesIndex).names());
permittedTypes.removeAll(DLSFLS);
if (log.isDebugEnabled()) {
log.debug(" matches for {}, will check now types {}", permittedAliasesIndex, permittedTypes);
}
for (final String type : permittedTypes) {
final Set<String> resolvedActions = resolveActions(permittedAliasesIndices.get(permittedAliasesIndex).getAsArray(type));
if (WildcardMatcher.matchAny(resolvedActions.toArray(new String[0]), action)) {
if (log.isDebugEnabled()) {
log.debug(" match requested action {} against {}/{}: {}", action, permittedAliasesIndex, type, resolvedActions);
}
for(String it: wi) {
boolean removed = wildcardRemoveFromSet(_requestedResolvedIndexTypes, new IndexType(it, type));
if(removed) {
log.debug(" removed {}", it+type);
} else {
log.debug(" no match {} in {}", it+type, _requestedResolvedIndexTypes);
}
}
}
}
} else {
if (log.isDebugEnabled()) {
log.debug(" No wildcard match found for {}", permittedAliasesIndex);
}
return;
}
}
private void handleIndicesWithoutWildcard(final String action, final String permittedAliasesIndex,
final Map<String, Settings> permittedAliasesIndices, final Set<IndexType> requestedResolvedIndexTypes, final Set<IndexType> _requestedResolvedIndexTypes) {
final Set<String> resolvedPermittedAliasesIndex = new HashSet<String>();
if(!resolver.hasIndexOrAlias(permittedAliasesIndex, clusterService.state())) {
if(log.isDebugEnabled()) {
log.debug("no permittedAliasesIndex '{}' found for '{}'", permittedAliasesIndex, action);
for(String pai: permittedAliasesIndices.keySet()) {
Settings paiSettings = permittedAliasesIndices.get(pai);
log.debug("permittedAliasesIndices '{}' -> '{}'", permittedAliasesIndices, paiSettings==null?"null":String.valueOf(paiSettings.getAsMap()));
}
log.debug("requestedResolvedIndexTypes '{}'", requestedResolvedIndexTypes);
}
resolvedPermittedAliasesIndex.add(permittedAliasesIndex);
} else {
resolvedPermittedAliasesIndex.addAll(Arrays.asList(resolver.concreteIndices(
clusterService.state(), DEFAULT_INDICES_OPTIONS, permittedAliasesIndex)));
}
if (log.isDebugEnabled()) {
log.debug(" resolved permitted aliases indices for {}: {}", permittedAliasesIndex, resolvedPermittedAliasesIndex);
}
//resolvedPermittedAliasesIndex -> resolved indices from role entry n
final Set<String> permittedTypes = new HashSet<String>(permittedAliasesIndices.get(permittedAliasesIndex).names());
permittedTypes.removeAll(DLSFLS);
if (log.isDebugEnabled()) {
log.debug(" matches for {}, will check now types {}", permittedAliasesIndex, permittedTypes);
}
for (final String type : permittedTypes) {
final Set<String> resolvedActions = resolveActions(permittedAliasesIndices.get(permittedAliasesIndex).getAsArray(type));
if (WildcardMatcher.matchAny(resolvedActions.toArray(new String[0]), action)) {
if (log.isDebugEnabled()) {
log.debug(" match requested action {} against {}/{}: {}", action, permittedAliasesIndex, type, resolvedActions);
}
for(String resolvedPermittedIndex: resolvedPermittedAliasesIndex) {
boolean removed = wildcardRemoveFromSet(_requestedResolvedIndexTypes, new IndexType(resolvedPermittedIndex, type));
if(removed) {
log.debug(" removed {}", resolvedPermittedIndex+type);
} else {
log.debug(" no match {} in {}", resolvedPermittedIndex+type, _requestedResolvedIndexTypes);
}
}
}
}
}
private Tuple<Set<String>, Set<String>> resolve(final User user, final String action, final TransportRequest request,
final MetaData metaData) {
if (!(request instanceof CompositeIndicesRequest) && !(request instanceof IndicesRequest)) {
if (log.isDebugEnabled()) {
log.debug("{} is not an IndicesRequest", request.getClass());
}
return new Tuple<Set<String>, Set<String>>(Sets.newHashSet("_all"), Sets.newHashSet("_all"));
}
final Set<String> indices = new HashSet<String>();
final Set<String> types = new HashSet<String>();
if (request instanceof CompositeIndicesRequest) {
for (final IndicesRequest indicesRequest : ((CompositeIndicesRequest) request).subRequests()) {
final Tuple<Set<String>, Set<String>> t = resolve(user, action, indicesRequest, metaData);
indices.addAll(t.v1());
types.addAll(t.v2());
}
} else {
final Tuple<Set<String>, Set<String>> t = resolve(user, action, (IndicesRequest) request, metaData);
indices.addAll(t.v1());
types.addAll(t.v2());
}
//for PutIndexTemplateRequest the index does not exists yet typically
if (IndexNameExpressionResolver.isAllIndices(new ArrayList<String>(indices))) {
if(log.isDebugEnabled()) {
log.debug("The following list are '_all' indices: {}", indices);
}
indices.clear();
indices.add("_all");
}
if (types.isEmpty()) {
types.add("_all");
}
return new Tuple<Set<String>, Set<String>>(Collections.unmodifiableSet(indices), Collections.unmodifiableSet(types));
}
private Tuple<Set<String>, Set<String>> resolve(final User user, final String action, final IndicesRequest request,
final MetaData metaData) {
if (log.isDebugEnabled()) {
log.debug("Resolve {} from {}", request.indices(), request.getClass());
}
final Class<? extends IndicesRequest> requestClass = request.getClass();
final Set<String> requestTypes = new HashSet<String>();
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);
}
}
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);
}
}
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 {}", e, 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 {}", e, requestClass, e);
}
}
if (log.isDebugEnabled()) {
log.debug("indicesOptions {}", request.indicesOptions());
log.debug("raw indices {}", Arrays.toString(request.indices()));
}
final Set<String> indices = new HashSet<String>();
if(request.indices() == null || request.indices().length == 0 || new HashSet<String>(Arrays.asList(request.indices())).equals(NULL_SET)) {
if(log.isDebugEnabled()) {
log.debug("No indices found in request, assume _all");
}
indices.addAll(Arrays.asList(resolver.concreteIndices(clusterService.state(), DEFAULT_INDICES_OPTIONS, "*")));
} else {
try {
indices.addAll(Arrays.asList(resolver.concreteIndices(clusterService.state(), request)));
if(log.isDebugEnabled()) {
log.debug("Resolved {} to {}", request.indices(), indices);
}
} catch (final Exception e) {
log.debug("Cannot resolve {} (due to {}) so we use the raw values", Arrays.toString(request.indices()), e);
indices.addAll(Arrays.asList(request.indices()));
}
}
return new Tuple<Set<String>, Set<String>>(indices, requestTypes);
}
private Set<String> resolveActions(final String[] actions) {
final Set<String> resolvedActions = new HashSet<String>();
for (int i = 0; i < actions.length; i++) {
final String string = actions[i];
final Set<String> groups = ah.getGroupMembers(string);
if (groups.isEmpty()) {
resolvedActions.add(string);
} else {
resolvedActions.addAll(groups);
}
}
return resolvedActions;
}
private boolean wildcardRemoveFromSet(Set<IndexType> set, IndexType stringContainingWc) {
if(set.contains(stringContainingWc)) {
return set.remove(stringContainingWc);
} else {
boolean modified = false;
Set<IndexType> copy = new HashSet<IndexType>(set);
for(IndexType it: copy) {
if(WildcardMatcher.match(stringContainingWc.getCombinedString(), it.getCombinedString())) {
modified = set.remove(it) | modified;
}
}
return modified;
}
}
}