Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.amazon.opendistroforelasticsearch.security.securityconf.ConfigModelV6 Maven / Gradle / Ivy
/*
* 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.amazon.opendistroforelasticsearch.security.securityconf;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress;
import com.amazon.opendistroforelasticsearch.security.resolver.IndexResolverReplacer.Resolved;
import com.amazon.opendistroforelasticsearch.security.securityconf.impl.SecurityDynamicConfiguration;
import com.amazon.opendistroforelasticsearch.security.securityconf.impl.v6.ActionGroupsV6;
import com.amazon.opendistroforelasticsearch.security.securityconf.impl.v6.RoleMappingsV6;
import com.amazon.opendistroforelasticsearch.security.securityconf.impl.v6.RoleV6;
import com.amazon.opendistroforelasticsearch.security.securityconf.impl.v6.RoleV6.Index;
import com.amazon.opendistroforelasticsearch.security.support.ConfigConstants;
import com.amazon.opendistroforelasticsearch.security.support.WildcardMatcher;
import com.amazon.opendistroforelasticsearch.security.user.User;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder.SetMultimapBuilder;
import com.google.common.collect.SetMultimap;
import static org.elasticsearch.cluster.metadata.IndexAbstraction.Type.ALIAS;
public class ConfigModelV6 extends ConfigModel {
protected final Logger log = LogManager.getLogger(this.getClass());
private ConfigConstants.RolesMappingResolution rolesMappingResolution;
private ActionGroupResolver agr = null;
private SecurityRoles securityRoles = null;
private TenantHolder tenantHolder;
private RoleMappingHolder roleMappingHolder;
private SecurityDynamicConfiguration roles;
public ConfigModelV6(
SecurityDynamicConfiguration roles,
SecurityDynamicConfiguration actiongroups,
SecurityDynamicConfiguration rolesmapping,
DynamicConfigModel dcm,
Settings esSettings) {
this.roles = roles;
try {
rolesMappingResolution = ConfigConstants.RolesMappingResolution.valueOf(
esSettings.get(ConfigConstants.OPENDISTRO_SECURITY_ROLES_MAPPING_RESOLUTION, ConfigConstants.RolesMappingResolution.MAPPING_ONLY.toString())
.toUpperCase());
} catch (Exception e) {
log.error("Cannot apply roles mapping resolution", e);
rolesMappingResolution = ConfigConstants.RolesMappingResolution.MAPPING_ONLY;
}
agr = reloadActionGroups(actiongroups);
securityRoles = reload(roles);
tenantHolder = new TenantHolder(roles);
roleMappingHolder = new RoleMappingHolder(rolesmapping, dcm.getHostsResolverMode());
}
public Set getAllConfiguredTenantNames() {
final Set configuredTenants = new HashSet<>();
for (Entry securityRole : roles.getCEntries().entrySet()) {
Map tenants = securityRole.getValue().getTenants();
if (tenants != null) {
configuredTenants.addAll(tenants.keySet());
}
}
return Collections.unmodifiableSet(configuredTenants);
}
public SecurityRoles getSecurityRoles() {
return securityRoles;
}
private static interface ActionGroupResolver {
Set resolvedActions(final List actions);
}
private ActionGroupResolver reloadActionGroups(SecurityDynamicConfiguration actionGroups) {
return new ActionGroupResolver() {
private Set getGroupMembers(final String groupname) {
if (actionGroups == null) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(resolve(actionGroups, groupname));
}
private Set resolve(final SecurityDynamicConfiguration> actionGroups, final String entry) {
// SG5 format, plain array
//List en = actionGroups.getAsList(DotPath.of(entry));
//if (en.isEmpty()) {
// try SG6 format including readonly and permissions key
// en = actionGroups.getAsList(DotPath.of(entry + "." + ConfigConstants.CONFIGKEY_ACTION_GROUPS_PERMISSIONS));
//}
if(!actionGroups.getCEntries().containsKey(entry)) {
return Collections.emptySet();
}
final Set ret = new HashSet();
final Object actionGroupAsObject = actionGroups.getCEntries().get(entry);
if(actionGroupAsObject != null && actionGroupAsObject instanceof List) {
for (final String perm: ((List) actionGroupAsObject)) {
if (actionGroups.getCEntries().keySet().contains(perm)) {
ret.addAll(resolve(actionGroups,perm));
} else {
ret.add(perm);
}
}
} else if(actionGroupAsObject != null && actionGroupAsObject instanceof ActionGroupsV6) {
for (final String perm: ((ActionGroupsV6) actionGroupAsObject).getPermissions()) {
if (actionGroups.getCEntries().keySet().contains(perm)) {
ret.addAll(resolve(actionGroups,perm));
} else {
ret.add(perm);
}
}
} else {
throw new RuntimeException("Unable to handle "+actionGroupAsObject);
}
return Collections.unmodifiableSet(ret);
}
@Override
public Set resolvedActions(final List actions) {
final Set resolvedActions = new HashSet();
for (String string: actions) {
final Set groups = getGroupMembers(string);
if (groups.isEmpty()) {
resolvedActions.add(string);
} else {
resolvedActions.addAll(groups);
}
}
return Collections.unmodifiableSet(resolvedActions);
}
};
}
private SecurityRoles reload(SecurityDynamicConfiguration settings) {
final Set> futures = new HashSet<>(5000);
final ExecutorService execs = Executors.newFixedThreadPool(10);
for(Entry securityRole: settings.getCEntries().entrySet()) {
Future future = execs.submit(new Callable() {
@Override
public SecurityRole call() throws Exception {
SecurityRole _securityRole = new SecurityRole(securityRole.getKey());
if(securityRole.getValue() == null) {
return null;
}
final Set permittedClusterActions = agr.resolvedActions(securityRole.getValue().getCluster());
_securityRole.addClusterPerms(permittedClusterActions);
//if(tenants != null) {
for(Entry tenant: securityRole.getValue().getTenants().entrySet()) {
//if(tenant.equals(user.getName())) {
// continue;
//}
if("RW".equalsIgnoreCase(tenant.getValue())) {
_securityRole.addTenant(new Tenant(tenant.getKey(), true));
} else {
_securityRole.addTenant(new Tenant(tenant.getKey(), false));
//if(_securityRole.tenants.stream().filter(t->t.tenant.equals(tenant)).count() > 0) { //RW outperforms RO
// _securityRole.addTenant(new Tenant(tenant, false));
//}
}
}
//}
//final Map permittedAliasesIndices = securityRoleSettings.getGroups(DotPath.of("indices"));
for (final Entry permittedAliasesIndex : securityRole.getValue().getIndices().entrySet()) {
//final String resolvedRole = securityRole;
//final String indexPattern = permittedAliasesIndex;
final String dls = permittedAliasesIndex.getValue().get_dls_();
final List fls = permittedAliasesIndex.getValue().get_fls_();
final List maskedFields = permittedAliasesIndex.getValue().get_masked_fields_();
IndexPattern _indexPattern = new IndexPattern(permittedAliasesIndex.getKey());
_indexPattern.setDlsQuery(dls);
_indexPattern.addFlsFields(fls);
_indexPattern.addMaskedFields(maskedFields);
for(Entry> type: permittedAliasesIndex.getValue().getTypes().entrySet()) {
TypePerm typePerm = new TypePerm(type.getKey());
final List perms = type.getValue();
typePerm.addPerms(agr.resolvedActions(perms));
_indexPattern.addTypePerms(typePerm);
}
_securityRole.addIndexPattern(_indexPattern);
}
return _securityRole;
}
});
futures.add(future);
}
execs.shutdown();
try {
execs.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Thread interrupted (1) while loading roles");
return null;
}
try {
SecurityRoles _securityRoles = new SecurityRoles(futures.size());
for (Future future : futures) {
_securityRoles.addSecurityRole(future.get());
}
return _securityRoles;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Thread interrupted (2) while loading roles");
return null;
} catch (ExecutionException e) {
log.error("Error while updating roles: {}", e.getCause(), e.getCause());
throw ExceptionsHelper.convertToElastic(e);
}
}
//beans
public static class SecurityRoles implements com.amazon.opendistroforelasticsearch.security.securityconf.SecurityRoles {
protected final Logger log = LogManager.getLogger(this.getClass());
final Set roles;
private SecurityRoles(int roleCount) {
roles = new HashSet<>(roleCount);
}
private SecurityRoles addSecurityRole(SecurityRole securityRole) {
if (securityRole != null) {
this.roles.add(securityRole);
}
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((roles == null) ? 0 : roles.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;
SecurityRoles other = (SecurityRoles) obj;
if (roles == null) {
if (other.roles != null)
return false;
} else if (!roles.equals(other.roles))
return false;
return true;
}
@Override
public String toString() {
return "roles=" + roles;
}
public Set getRoles() {
return Collections.unmodifiableSet(roles);
}
public Set getRoleNames() {
return getRoles().stream().map(r -> r.getName()).collect(Collectors.toSet());
}
public SecurityRoles filter(Set keep) {
final SecurityRoles retVal = new SecurityRoles(roles.size());
for (SecurityRole sr : roles) {
if (keep.contains(sr.getName())) {
retVal.addSecurityRole(sr);
}
}
return retVal;
}
@Override
public Map> getMaskedFields(User user, IndexNameExpressionResolver resolver, ClusterService cs) {
final Map> maskedFieldsMap = new HashMap<>();
for (SecurityRole sr : roles) {
for (IndexPattern ip : sr.getIpatterns()) {
final Set maskedFields = ip.getMaskedFields();
if (!maskedFields.isEmpty()) {
final String indexPattern = ip.getUnresolvedIndexPattern(user);
Set concreteIndices = ip.getResolvedIndexPattern(user, resolver, cs);
Set currentMaskedFields = maskedFieldsMap.get(indexPattern);
if (currentMaskedFields != null) {
currentMaskedFields.addAll(maskedFields);
} else {
maskedFieldsMap.put(indexPattern, new HashSet<>(maskedFields));
}
for (String concreteIndex : concreteIndices) {
currentMaskedFields = maskedFieldsMap.get(concreteIndex);
if (currentMaskedFields != null) {
currentMaskedFields.addAll(maskedFields);
} else {
maskedFieldsMap.put(concreteIndex, new HashSet<>(maskedFields));
}
}
}
}
}
return maskedFieldsMap;
}
@Override
public Tuple>, Map>> getDlsFls(User user, IndexNameExpressionResolver resolver,
ClusterService cs) {
final Map> dlsQueries = new HashMap>();
final Map> flsFields = new HashMap>();
for (SecurityRole sr : roles) {
for (IndexPattern ip : sr.getIpatterns()) {
final Set fls = ip.getFls();
final String dls = ip.getDlsQuery(user);
final String indexPattern = ip.getUnresolvedIndexPattern(user);
Set concreteIndices = new HashSet<>();
if ((dls != null && dls.length() > 0) || (fls != null && fls.size() > 0)) {
concreteIndices = ip.getResolvedIndexPattern(user, resolver, cs);
}
if (dls != null && dls.length() > 0) {
Set dlsQuery = dlsQueries.get(indexPattern);
if (dlsQuery != null) {
dlsQuery.add(dls);
} else {
dlsQueries.put(indexPattern, new HashSet<>(Arrays.asList(dls)));
}
for (String concreteIndex : concreteIndices) {
dlsQuery = dlsQueries.get(concreteIndex);
if (dlsQuery != null) {
dlsQuery.add(dls);
} else {
dlsQueries.put(concreteIndex, new HashSet<>(Arrays.asList(dls)));
}
}
}
if (fls != null && fls.size() > 0) {
Set flsField = flsFields.get(indexPattern);
if (flsField != null) {
flsField.addAll(fls);
} else {
flsFields.put(indexPattern, new HashSet<>(fls));
}
for (String concreteIndex : concreteIndices) {
flsField = flsFields.get(concreteIndex);
if (flsField != null) {
flsField.addAll(fls);
} else {
flsFields.put(concreteIndex, new HashSet<>(fls));
}
}
}
}
}
return new Tuple<>(dlsQueries, flsFields);
}
//kibana special only, terms eval
public Set getAllPermittedIndicesForKibana(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) {
Set retVal = new HashSet<>();
for (SecurityRole sr : roles) {
retVal.addAll(sr.getAllResolvedPermittedIndices(Resolved._LOCAL_ALL, user, actions, resolver, cs));
retVal.addAll(resolved.getRemoteIndices());
}
return Collections.unmodifiableSet(retVal);
}
//dnfof only
public Set reduce(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) {
Set retVal = new HashSet<>();
for (SecurityRole sr : roles) {
retVal.addAll(sr.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs));
}
if (log.isDebugEnabled()) {
log.debug("Reduced requested resolved indices {} to permitted indices {}.", resolved, retVal.toString());
}
return Collections.unmodifiableSet(retVal);
}
//return true on success
public boolean get(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver, ClusterService cs) {
for (SecurityRole sr : roles) {
if (ConfigModelV6.impliesTypePerm(sr.getIpatterns(), resolved, user, actions, resolver, cs)) {
return true;
}
}
return false;
}
public boolean impliesClusterPermissionPermission(String action) {
return roles.stream().filter(r -> r.impliesClusterPermission(action)).count() > 0;
}
//rolespan
public boolean impliesTypePermGlobal(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver,
ClusterService cs) {
Set ipatterns = new HashSet();
roles.stream().forEach(p -> ipatterns.addAll(p.getIpatterns()));
return ConfigModelV6.impliesTypePerm(ipatterns, resolved, user, actions, resolver, cs);
}
}
public static class SecurityRole {
private final String name;
private final Set tenants = new HashSet<>();
private final Set ipatterns = new HashSet<>();
private final Set clusterPerms = new HashSet<>();
private SecurityRole(String name) {
super();
this.name = Objects.requireNonNull(name);
}
private boolean impliesClusterPermission(String action) {
return WildcardMatcher.from(clusterPerms).test(action);
}
//get indices which are permitted for the given types and actions
//dnfof + kibana special only
private Set getAllResolvedPermittedIndices(Resolved resolved, User user, String[] actions, IndexNameExpressionResolver resolver,
ClusterService cs) {
final Set retVal = new HashSet<>();
for (IndexPattern p : ipatterns) {
//what if we cannot resolve one (for create purposes)
boolean patternMatch = false;
final Set tperms = p.getTypePerms();
for (TypePerm tp : tperms) {
if (tp.typeMatcher.matchAny(resolved.getTypes())) {
patternMatch = tp.getPerms().matchAll(actions);
}
}
if (patternMatch) {
//resolved but can contain patterns for nonexistent indices
final WildcardMatcher permitted = WildcardMatcher.from(p.getResolvedIndexPattern(user, resolver, cs)); //maybe they do not exist
final Set res = new HashSet<>();
if (!resolved.isLocalAll() && !resolved.getAllIndices().contains("*") && !resolved.getAllIndices().contains("_all")) {
//resolved but can contain patterns for nonexistent indices
resolved.getAllIndices().stream().filter(permitted).forEach(res::add);
} else {
//we want all indices so just return what's permitted
//#557
//final String[] allIndices = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), "*");
Arrays.stream(cs.state().metadata().getConcreteAllOpenIndices()).filter(permitted).forEach(res::add);
}
retVal.addAll(res);
}
}
//all that we want and all thats permitted of them
return Collections.unmodifiableSet(retVal);
}
private SecurityRole addTenant(Tenant tenant) {
if (tenant != null) {
this.tenants.add(tenant);
}
return this;
}
private SecurityRole addIndexPattern(IndexPattern indexPattern) {
if (indexPattern != null) {
this.ipatterns.add(indexPattern);
}
return this;
}
private SecurityRole addClusterPerms(Collection clusterPerms) {
if (clusterPerms != null) {
this.clusterPerms.addAll(clusterPerms);
}
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((clusterPerms == null) ? 0 : clusterPerms.hashCode());
result = prime * result + ((ipatterns == null) ? 0 : ipatterns.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((tenants == null) ? 0 : tenants.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;
SecurityRole other = (SecurityRole) obj;
if (clusterPerms == null) {
if (other.clusterPerms != null)
return false;
} else if (!clusterPerms.equals(other.clusterPerms))
return false;
if (ipatterns == null) {
if (other.ipatterns != null)
return false;
} else if (!ipatterns.equals(other.ipatterns))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (tenants == null) {
if (other.tenants != null)
return false;
} else if (!tenants.equals(other.tenants))
return false;
return true;
}
@Override
public String toString() {
return System.lineSeparator() + " " + name + System.lineSeparator() + " tenants=" + tenants + System.lineSeparator()
+ " ipatterns=" + ipatterns + System.lineSeparator() + " clusterPerms=" + clusterPerms;
}
public Set getTenants(User user) {
//TODO filter out user tenants
return Collections.unmodifiableSet(tenants);
}
public Set getIpatterns() {
return Collections.unmodifiableSet(ipatterns);
}
public Set getClusterPerms() {
return Collections.unmodifiableSet(clusterPerms);
}
public String getName() {
return name;
}
}
//sg roles
public static class IndexPattern {
private final String indexPattern;
private String dlsQuery;
private final Set fls = new HashSet<>();
private final Set maskedFields = new HashSet<>();
private final Set typePerms = new HashSet<>();
public IndexPattern(String indexPattern) {
super();
this.indexPattern = Objects.requireNonNull(indexPattern);
}
public IndexPattern addFlsFields(List flsFields) {
if (flsFields != null) {
this.fls.addAll(flsFields);
}
return this;
}
public IndexPattern addMaskedFields(List maskedFields) {
if (maskedFields != null) {
this.maskedFields.addAll(maskedFields);
}
return this;
}
public IndexPattern addTypePerms(TypePerm typePerm) {
if (typePerm != null) {
this.typePerms.add(typePerm);
}
return this;
}
public IndexPattern setDlsQuery(String dlsQuery) {
if (dlsQuery != null) {
this.dlsQuery = dlsQuery;
}
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((dlsQuery == null) ? 0 : dlsQuery.hashCode());
result = prime * result + ((fls == null) ? 0 : fls.hashCode());
result = prime * result + ((maskedFields == null) ? 0 : maskedFields.hashCode());
result = prime * result + ((indexPattern == null) ? 0 : indexPattern.hashCode());
result = prime * result + ((typePerms == null) ? 0 : typePerms.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;
IndexPattern other = (IndexPattern) obj;
if (dlsQuery == null) {
if (other.dlsQuery != null)
return false;
} else if (!dlsQuery.equals(other.dlsQuery))
return false;
if (fls == null) {
if (other.fls != null)
return false;
} else if (!fls.equals(other.fls))
return false;
if (maskedFields == null) {
if (other.maskedFields != null)
return false;
} else if (!maskedFields.equals(other.maskedFields))
return false;
if (indexPattern == null) {
if (other.indexPattern != null)
return false;
} else if (!indexPattern.equals(other.indexPattern))
return false;
if (typePerms == null) {
if (other.typePerms != null)
return false;
} else if (!typePerms.equals(other.typePerms))
return false;
return true;
}
@Override
public String toString() {
return System.lineSeparator() + " indexPattern=" + indexPattern + System.lineSeparator() + " dlsQuery=" + dlsQuery
+ System.lineSeparator() + " fls=" + fls + System.lineSeparator() + " typePerms=" + typePerms;
}
public String getUnresolvedIndexPattern(User user) {
return replaceProperties(indexPattern, user);
}
private Set getResolvedIndexPattern(User user, IndexNameExpressionResolver resolver, ClusterService cs) {
String unresolved = getUnresolvedIndexPattern(user);
WildcardMatcher matcher = WildcardMatcher.from(unresolved);
String[] resolved = null;
if (!(matcher instanceof WildcardMatcher.Exact)) {
final String[] aliasesForPermittedPattern = cs.state().getMetadata().getIndicesLookup().entrySet().stream()
.filter(e -> e.getValue().getType() == ALIAS)
.filter(e -> matcher.test(e.getKey()))
.map(e -> e.getKey())
.toArray(String[]::new);
if (aliasesForPermittedPattern.length > 0) {
resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), aliasesForPermittedPattern);
}
}
if (resolved == null && !unresolved.isEmpty()) {
resolved = resolver.concreteIndexNames(cs.state(), IndicesOptions.lenientExpandOpen(), unresolved);
}
if (resolved == null || resolved.length == 0) {
return ImmutableSet.of(unresolved);
} else {
return ImmutableSet.builder()
.addAll(Arrays.asList(resolved))
.add(unresolved)
.build();
}
}
public String getDlsQuery(User user) {
return replaceProperties(dlsQuery, user);
}
public Set getFls() {
return Collections.unmodifiableSet(fls);
}
public Set getMaskedFields() {
return Collections.unmodifiableSet(maskedFields);
}
public Set getTypePerms() {
return Collections.unmodifiableSet(typePerms);
}
}
public static class TypePerm {
private final WildcardMatcher typeMatcher;
private final Set perms = new HashSet<>();
private TypePerm(String typePattern) {
this.typeMatcher = WildcardMatcher.ANY;
}
private TypePerm addPerms(Collection perms) {
if (perms != null) {
this.perms.addAll(perms);
}
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((perms == null) ? 0 : perms.hashCode());
result = prime * result + ((typeMatcher == null) ? 0 : typeMatcher.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;
TypePerm other = (TypePerm) obj;
if (perms == null) {
if (other.perms != null)
return false;
} else if (!perms.equals(other.perms))
return false;
if (typeMatcher == null) {
if (other.typeMatcher != null)
return false;
} else if (!typeMatcher.equals(other.typeMatcher))
return false;
return true;
}
@Override
public String toString() {
return System.lineSeparator() + " typePattern=" + typeMatcher + System.lineSeparator() + " perms=" + perms;
}
public WildcardMatcher getTypeMatcher() {
return typeMatcher;
}
public WildcardMatcher getPerms() {
return WildcardMatcher.from(perms);
}
}
public static class Tenant {
private final String tenant;
private final boolean readWrite;
private Tenant(String tenant, boolean readWrite) {
super();
this.tenant = tenant;
this.readWrite = readWrite;
}
public String getTenant() {
return tenant;
}
public boolean isReadWrite() {
return readWrite;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (readWrite ? 1231 : 1237);
result = prime * result + ((tenant == null) ? 0 : tenant.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;
Tenant other = (Tenant) obj;
if (readWrite != other.readWrite)
return false;
if (tenant == null) {
if (other.tenant != null)
return false;
} else if (!tenant.equals(other.tenant))
return false;
return true;
}
@Override
public String toString() {
return System.lineSeparator() + " tenant=" + tenant + System.lineSeparator() + " readWrite=" + readWrite;
}
}
private static String replaceProperties(String orig, User user) {
if (user == null || orig == null) {
return orig;
}
orig = orig.replace("${user.name}", user.getName()).replace("${user_name}", user.getName());
orig = replaceRoles(orig, user);
for (Entry entry : user.getCustomAttributesMap().entrySet()) {
if (entry == null || entry.getKey() == null || entry.getValue() == null) {
continue;
}
orig = orig.replace("${" + entry.getKey() + "}", entry.getValue());
orig = orig.replace("${" + entry.getKey().replace('.', '_') + "}", entry.getValue());
}
return orig;
}
private static String replaceRoles(final String orig, final User user) {
String retVal = orig;
if (orig.contains("${user.roles}") || orig.contains("${user_roles}")) {
final String commaSeparatedRoles = toQuotedCommaSeparatedString(user.getRoles());
retVal = orig.replace("${user.roles}", commaSeparatedRoles).replace("${user_roles}", commaSeparatedRoles);
}
return retVal;
}
private static String toQuotedCommaSeparatedString(final Set roles) {
return Joiner.on(',').join(Iterables.transform(roles, s -> {
return new StringBuilder(s.length() + 2).append('"').append(s).append('"').toString();
}));
}
private static final class IndexMatcherAndTypePermissions {
private static final Logger log = LogManager.getLogger(IndexMatcherAndTypePermissions.class);
private final WildcardMatcher matcher;
private final Set typePerms;
public IndexMatcherAndTypePermissions(Set pattern, Set typePerms) {
this.matcher = WildcardMatcher.from(pattern);
this.typePerms = typePerms;
}
private static String b2s(boolean matches) {
return matches ? "matches" : "does not match";
}
public boolean matches(String index, String type, String action) {
boolean matchIndex = matcher.test(index);
if (log.isDebugEnabled()) {
log.debug("index {} {} index pattern {}", index, b2s(matchIndex), matcher);
}
if (matchIndex) {
return typePerms.stream().anyMatch(tp -> {
boolean matchType = tp.getTypeMatcher().test(type);
if (log.isDebugEnabled()) {
log.debug("type {} {} type pattern {}", type, b2s(matchType), tp.getTypeMatcher());
}
if (matchType) {
boolean matchAction = tp.getPerms().test(action);
if (log.isDebugEnabled()) {
log.debug("action {} {} action pattern {}", action, b2s(matchAction), tp.getPerms());
}
return matchAction;
}
return false;
});
}
return false;
}
}
private static boolean impliesTypePerm(Set ipatterns, Resolved resolved, User user, String[] requestedActions,
IndexNameExpressionResolver resolver, ClusterService cs) {
IndexMatcherAndTypePermissions[] indexMatcherAndTypePermissions = ipatterns
.stream()
.map(p -> new IndexMatcherAndTypePermissions(p.getResolvedIndexPattern(user, resolver, cs), p.getTypePerms()))
.toArray(IndexMatcherAndTypePermissions[]::new);
return resolved.getAllIndices()
.stream().allMatch(index ->
resolved.getTypes().stream().allMatch(type ->
Arrays.stream(requestedActions).allMatch(action ->
Arrays.stream(indexMatcherAndTypePermissions).anyMatch(ipatp ->
ipatp.matches(index, type, action)
)
)
)
);
}
//#######
private class TenantHolder {
private SetMultimap> tenantsMM = null;
public TenantHolder(SecurityDynamicConfiguration roles) {
final Set>>>> futures = new HashSet<>(roles.getCEntries().size());
final ExecutorService execs = Executors.newFixedThreadPool(10);
for(Entry securityRole: roles.getCEntries().entrySet()) {
if(securityRole.getValue() == null) {
continue;
}
Future>>> future = execs.submit(new Callable>>>() {
@Override
public Tuple>> call() throws Exception {
final Set> tuples = new HashSet<>();
final Map tenants = securityRole.getValue().getTenants();
if (tenants != null) {
for (String tenant : tenants.keySet()) {
if ("RW".equalsIgnoreCase(tenants.get(tenant))) {
//RW
tuples.add(new Tuple(tenant, true));
} else {
//RO
//if(!tenantsMM.containsValue(value)) { //RW outperforms RO
tuples.add(new Tuple(tenant, false));
//}
}
}
}
return new Tuple>>(securityRole.getKey(), tuples);
}
});
futures.add(future);
}
execs.shutdown();
try {
execs.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Thread interrupted (1) while loading roles");
return;
}
try {
final SetMultimap> tenantsMM_ = SetMultimapBuilder.hashKeys(futures.size()).hashSetValues(16).build();
for (Future>>> future : futures) {
Tuple>> result = future.get();
tenantsMM_.putAll(result.v1(), result.v2());
}
tenantsMM = tenantsMM_;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("Thread interrupted (2) while loading roles");
return;
} catch (ExecutionException e) {
log.error("Error while updating roles: {}", e.getCause(), e.getCause());
throw ExceptionsHelper.convertToElastic(e);
}
}
public Map mapTenants(final User user, Set roles) {
if (user == null || tenantsMM == null) {
return Collections.emptyMap();
}
final Map result = new HashMap<>(roles.size());
result.put(user.getName(), true);
tenantsMM.entries().stream().filter(e -> roles.contains(e.getKey())).filter(e -> !user.getName().equals(e.getValue().v1())).forEach(e -> {
final String tenant = e.getValue().v1();
final boolean rw = e.getValue().v2();
if (rw || !result.containsKey(tenant)) { //RW outperforms RO
result.put(tenant, rw);
}
});
return Collections.unmodifiableMap(result);
}
}
private class RoleMappingHolder {
private ListMultimap users;
private ListMultimap, String> abars;
private ListMultimap bars;
private ListMultimap hosts;
private final String hostResolverMode;
private RoleMappingHolder(final SecurityDynamicConfiguration rolesMapping, final String hostResolverMode) {
this.hostResolverMode = hostResolverMode;
if (rolesMapping != null) {
final ListMultimap users_ = ArrayListMultimap.create();
final ListMultimap, String> abars_ = ArrayListMultimap.create();
final ListMultimap bars_ = ArrayListMultimap.create();
final ListMultimap hosts_ = ArrayListMultimap.create();
for (final Entry roleMap : rolesMapping.getCEntries().entrySet()) {
for (String u : roleMap.getValue().getUsers()) {
users_.put(u, roleMap.getKey());
}
final Set abar = new HashSet(roleMap.getValue().getAndBackendroles());
if (!abar.isEmpty()) {
abars_.put(abar, roleMap.getKey());
}
for (String bar : roleMap.getValue().getBackendroles()) {
bars_.put(bar, roleMap.getKey());
}
for (String host : roleMap.getValue().getHosts()) {
hosts_.put(host, roleMap.getKey());
}
}
users = users_;
abars = abars_;
bars = bars_;
hosts = hosts_;
}
}
private Set map(final User user, final TransportAddress caller) {
if (user == null || users == null || abars == null || bars == null || hosts == null) {
return Collections.emptySet();
}
final Set securityRoles = new TreeSet();
if (rolesMappingResolution == ConfigConstants.RolesMappingResolution.BOTH
|| rolesMappingResolution == ConfigConstants.RolesMappingResolution.BACKENDROLES_ONLY) {
if (log.isDebugEnabled()) {
log.debug("Pass backendroles from {}", user);
}
securityRoles.addAll(user.getRoles());
}
if (((rolesMappingResolution == ConfigConstants.RolesMappingResolution.BOTH
|| rolesMappingResolution == ConfigConstants.RolesMappingResolution.MAPPING_ONLY))) {
for (String p : WildcardMatcher.getAllMatchingPatterns(WildcardMatcher.matchers(users.keySet()), user.getName())) {
securityRoles.addAll(users.get(p));
}
for (String p : WildcardMatcher.getAllMatchingPatterns(WildcardMatcher.matchers(bars.keySet()), user.getRoles())) {
securityRoles.addAll(bars.get(p));
}
for (Set patterns : abars.keySet()) {
if (patterns.stream().allMatch(p -> WildcardMatcher.from(p).matchAny(user.getRoles()))) {
securityRoles.addAll(abars.get(patterns));
}
}
if (caller != null) {
//IPV4 or IPv6 (compressed and without scope identifiers)
final String ipAddress = caller.getAddress();
final List hostMatchers = WildcardMatcher.matchers(hosts.keySet());
for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, ipAddress)) {
securityRoles.addAll(hosts.get(p));
}
if (caller.address() != null
&& (hostResolverMode.equalsIgnoreCase("ip-hostname") || hostResolverMode.equalsIgnoreCase("ip-hostname-lookup"))) {
final String hostName = caller.address().getHostString();
for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, hostName)) {
securityRoles.addAll(hosts.get(p));
}
}
if (caller.address() != null && hostResolverMode.equalsIgnoreCase("ip-hostname-lookup")) {
final String resolvedHostName = caller.address().getHostName();
for (String p : WildcardMatcher.getAllMatchingPatterns(hostMatchers, resolvedHostName)) {
securityRoles.addAll(hosts.get(p));
}
}
}
}
return Collections.unmodifiableSet(securityRoles);
}
}
public Map mapTenants(User user, Set roles) {
return tenantHolder.mapTenants(user, roles);
}
public Set mapSecurityRoles(User user, TransportAddress caller) {
return roleMappingHolder.map(user, caller);
}
}