Fix merging of field level security rules (elastic/x-pack-elasticsearch#796)
This commit fixes the merging of field level security rules from multiple roles. Prior to 5.2, the merging was treated as the merging of languages whereas after 5.2, this incorrectly became a merge of all rules meaning a single wildcard could cause denials to be ignored. Original commit: elastic/x-pack-elasticsearch@42f9e6d8b0
This commit is contained in:
parent
3f5d9850ae
commit
8ba6e8b3eb
|
@ -14,19 +14,20 @@ import org.apache.lucene.util.automaton.CharacterRunAutomaton;
|
||||||
import org.apache.lucene.util.automaton.MinimizationOperations;
|
import org.apache.lucene.util.automaton.MinimizationOperations;
|
||||||
import org.apache.lucene.util.automaton.Operations;
|
import org.apache.lucene.util.automaton.Operations;
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
import org.elasticsearch.common.Nullable;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
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.regex.Regex;
|
import org.elasticsearch.common.regex.Regex;
|
||||||
import org.elasticsearch.index.mapper.AllFieldMapper;
|
import org.elasticsearch.index.mapper.AllFieldMapper;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
|
||||||
import org.elasticsearch.xpack.security.authz.accesscontrol.FieldSubsetReader;
|
import org.elasticsearch.xpack.security.authz.accesscontrol.FieldSubsetReader;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition.FieldGrantExcludeGroup;
|
||||||
import org.elasticsearch.xpack.security.support.Automatons;
|
import org.elasticsearch.xpack.security.support.Automatons;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.apache.lucene.util.automaton.Operations.subsetOf;
|
import static org.apache.lucene.util.automaton.Operations.subsetOf;
|
||||||
import static org.elasticsearch.xpack.security.support.Automatons.minusAndMinimize;
|
import static org.elasticsearch.xpack.security.support.Automatons.minusAndMinimize;
|
||||||
|
@ -39,73 +40,74 @@ import static org.elasticsearch.xpack.security.support.Automatons.minusAndMinimi
|
||||||
* 1. It has to match the patterns in grantedFieldsArray
|
* 1. It has to match the patterns in grantedFieldsArray
|
||||||
* 2. it must not match the patterns in deniedFieldsArray
|
* 2. it must not match the patterns in deniedFieldsArray
|
||||||
*/
|
*/
|
||||||
public final class FieldPermissions implements Writeable, Accountable {
|
public final class FieldPermissions implements Accountable {
|
||||||
|
|
||||||
public static final FieldPermissions DEFAULT = new FieldPermissions();
|
public static final FieldPermissions DEFAULT = new FieldPermissions();
|
||||||
|
|
||||||
// the patterns for fields which we allow access to. if gratedFieldsArray is null we assume that all fields are grated access to
|
private static final long BASE_FIELD_PERM_DEF_BYTES = RamUsageEstimator.shallowSizeOf(new FieldPermissionsDefinition(null, null));
|
||||||
private final String[] grantedFieldsArray;
|
private static final long BASE_FIELD_GROUP_BYTES = RamUsageEstimator.shallowSizeOf(new FieldGrantExcludeGroup(null, null));
|
||||||
// the patterns for fields which we deny access to. if this is an empty list or null we assume that we do not deny access to any
|
private static final long BASE_HASHSET_SIZE = RamUsageEstimator.shallowSizeOfInstance(HashSet.class);
|
||||||
// field explicitly
|
private static final long BASE_HASHSET_ENTRY_SIZE;
|
||||||
private final String[] deniedFieldsArray;
|
static {
|
||||||
// an automaton that matches all strings that match the patterns in permittedFieldsArray but does not match those that also match a
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
// pattern in deniedFieldsArray. If permittedFieldsAutomaton is null we assume that all fields are granted access to.
|
map.put(FieldPermissions.class.getName(), new Object());
|
||||||
|
long mapEntryShallowSize = RamUsageEstimator.shallowSizeOf(map.entrySet().iterator().next());
|
||||||
|
// assume a load factor of 50%
|
||||||
|
// for each entry, we need two object refs, one for the entry itself
|
||||||
|
// and one for the free space that is due to the fact hash tables can
|
||||||
|
// not be fully loaded
|
||||||
|
BASE_HASHSET_ENTRY_SIZE = mapEntryShallowSize + 2 * RamUsageEstimator.NUM_BYTES_OBJECT_REF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final FieldPermissionsDefinition fieldPermissionsDefinition;
|
||||||
|
// an automaton that represents a union of one more sets of permitted and denied fields
|
||||||
private final CharacterRunAutomaton permittedFieldsAutomaton;
|
private final CharacterRunAutomaton permittedFieldsAutomaton;
|
||||||
private final boolean permittedFieldsAutomatonIsTotal;
|
private final boolean permittedFieldsAutomatonIsTotal;
|
||||||
|
private final Automaton originalAutomaton;
|
||||||
|
|
||||||
private final long ramBytesUsed;
|
private final long ramBytesUsed;
|
||||||
|
|
||||||
/** Constructor that does not enable field-level security: all fields are accepted. */
|
/** Constructor that does not enable field-level security: all fields are accepted. */
|
||||||
public FieldPermissions() {
|
public FieldPermissions() {
|
||||||
this(null, null);
|
this(new FieldPermissionsDefinition(null, null), Automatons.MATCH_ALL);
|
||||||
}
|
|
||||||
|
|
||||||
public FieldPermissions(StreamInput in) throws IOException {
|
|
||||||
this(in.readOptionalStringArray(), in.readOptionalStringArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Constructor that enables field-level security based on include/exclude rules. Exclude rules
|
/** Constructor that enables field-level security based on include/exclude rules. Exclude rules
|
||||||
* have precedence over include rules. */
|
* have precedence over include rules. */
|
||||||
public FieldPermissions(@Nullable String[] grantedFieldsArray, @Nullable String[] deniedFieldsArray) {
|
public FieldPermissions(FieldPermissionsDefinition fieldPermissionsDefinition) {
|
||||||
this(grantedFieldsArray, deniedFieldsArray, initializePermittedFieldsAutomaton(grantedFieldsArray, deniedFieldsArray));
|
this(fieldPermissionsDefinition, initializePermittedFieldsAutomaton(fieldPermissionsDefinition));
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldPermissions(@Nullable String[] grantedFieldsArray, @Nullable String[] deniedFieldsArray,
|
/** Constructor that enables field-level security based on include/exclude rules. Exclude rules
|
||||||
Automaton permittedFieldsAutomaton) {
|
* have precedence over include rules. */
|
||||||
|
FieldPermissions(FieldPermissionsDefinition fieldPermissionsDefinition, Automaton permittedFieldsAutomaton) {
|
||||||
if (permittedFieldsAutomaton.isDeterministic() == false && permittedFieldsAutomaton.getNumStates() > 1) {
|
if (permittedFieldsAutomaton.isDeterministic() == false && permittedFieldsAutomaton.getNumStates() > 1) {
|
||||||
// we only accept deterministic automata so that the CharacterRunAutomaton constructor
|
// we only accept deterministic automata so that the CharacterRunAutomaton constructor
|
||||||
// directly wraps the provided automaton
|
// directly wraps the provided automaton
|
||||||
throw new IllegalArgumentException("Only accepts deterministic automata");
|
throw new IllegalArgumentException("Only accepts deterministic automata");
|
||||||
}
|
}
|
||||||
this.grantedFieldsArray = grantedFieldsArray;
|
this.fieldPermissionsDefinition = fieldPermissionsDefinition;
|
||||||
this.deniedFieldsArray = deniedFieldsArray;
|
this.originalAutomaton = permittedFieldsAutomaton;
|
||||||
this.permittedFieldsAutomaton = new CharacterRunAutomaton(permittedFieldsAutomaton);
|
this.permittedFieldsAutomaton = new CharacterRunAutomaton(permittedFieldsAutomaton);
|
||||||
// we cache the result of isTotal since this might be a costly operation
|
// we cache the result of isTotal since this might be a costly operation
|
||||||
this.permittedFieldsAutomatonIsTotal = Operations.isTotal(permittedFieldsAutomaton);
|
this.permittedFieldsAutomatonIsTotal = Operations.isTotal(permittedFieldsAutomaton);
|
||||||
|
|
||||||
long ramBytesUsed = ramBytesUsed(grantedFieldsArray);
|
long ramBytesUsed = BASE_FIELD_PERM_DEF_BYTES;
|
||||||
ramBytesUsed += ramBytesUsed(deniedFieldsArray);
|
|
||||||
|
for (FieldGrantExcludeGroup group : fieldPermissionsDefinition.getFieldGrantExcludeGroups()) {
|
||||||
|
ramBytesUsed += BASE_FIELD_GROUP_BYTES + BASE_HASHSET_ENTRY_SIZE;
|
||||||
|
if (group.getGrantedFields() != null) {
|
||||||
|
ramBytesUsed += RamUsageEstimator.shallowSizeOf(group.getGrantedFields());
|
||||||
|
}
|
||||||
|
if (group.getExcludedFields() != null) {
|
||||||
|
ramBytesUsed += RamUsageEstimator.shallowSizeOf(group.getExcludedFields());
|
||||||
|
}
|
||||||
|
}
|
||||||
ramBytesUsed += permittedFieldsAutomaton.ramBytesUsed();
|
ramBytesUsed += permittedFieldsAutomaton.ramBytesUsed();
|
||||||
ramBytesUsed += runAutomatonRamBytesUsed(permittedFieldsAutomaton);
|
ramBytesUsed += runAutomatonRamBytesUsed(permittedFieldsAutomaton);
|
||||||
this.ramBytesUsed = ramBytesUsed;
|
this.ramBytesUsed = ramBytesUsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an estimation of the ram bytes used by the given {@link String}
|
|
||||||
* array.
|
|
||||||
*/
|
|
||||||
private static long ramBytesUsed(String[] array) {
|
|
||||||
long ramBytesUsed = 0;
|
|
||||||
if (array != null) {
|
|
||||||
ramBytesUsed += RamUsageEstimator.shallowSizeOf(array);
|
|
||||||
for (String s : array) {
|
|
||||||
// might be overestimated because of compact strings but it is better to overestimate here
|
|
||||||
ramBytesUsed += s.length() * Character.BYTES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ramBytesUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an estimation of the ram bytes used by a {@link CharacterRunAutomaton}
|
* Return an estimation of the ram bytes used by a {@link CharacterRunAutomaton}
|
||||||
* that wraps the given automaton.
|
* that wraps the given automaton.
|
||||||
|
@ -114,22 +116,32 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
return a.getNumStates() * 5; // wild guess, better than 0
|
return a.getNumStates() * 5; // wild guess, better than 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Automaton initializePermittedFieldsAutomaton(final String[] grantedFieldsArray, final String[] deniedFieldsArray) {
|
static Automaton initializePermittedFieldsAutomaton(FieldPermissionsDefinition fieldPermissionsDefinition) {
|
||||||
|
Set<FieldGrantExcludeGroup> groups = fieldPermissionsDefinition.getFieldGrantExcludeGroups();
|
||||||
|
assert groups.size() > 0 : "there must always be a single group for field inclusion/exclusion";
|
||||||
|
List<Automaton> automatonList =
|
||||||
|
groups.stream()
|
||||||
|
.map(g -> FieldPermissions.initializePermittedFieldsAutomaton(g.getGrantedFields(), g.getExcludedFields()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return Automatons.unionAndMinimize(automatonList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Automaton initializePermittedFieldsAutomaton(final String[] grantedFields, final String[] deniedFields) {
|
||||||
Automaton grantedFieldsAutomaton;
|
Automaton grantedFieldsAutomaton;
|
||||||
if (grantedFieldsArray == null || Arrays.stream(grantedFieldsArray).anyMatch(Regex::isMatchAllPattern)) {
|
if (grantedFields == null || Arrays.stream(grantedFields).anyMatch(Regex::isMatchAllPattern)) {
|
||||||
grantedFieldsAutomaton = Automatons.MATCH_ALL;
|
grantedFieldsAutomaton = Automatons.MATCH_ALL;
|
||||||
} else {
|
} else {
|
||||||
// an automaton that includes metadata fields, including join fields created by the _parent field such
|
// an automaton that includes metadata fields, including join fields created by the _parent field such
|
||||||
// as _parent#type
|
// as _parent#type
|
||||||
Automaton metaFieldsAutomaton = Operations.concatenate(Automata.makeChar('_'), Automata.makeAnyString());
|
Automaton metaFieldsAutomaton = Operations.concatenate(Automata.makeChar('_'), Automata.makeAnyString());
|
||||||
grantedFieldsAutomaton = Operations.union(Automatons.patterns(grantedFieldsArray), metaFieldsAutomaton);
|
grantedFieldsAutomaton = Operations.union(Automatons.patterns(grantedFields), metaFieldsAutomaton);
|
||||||
}
|
}
|
||||||
|
|
||||||
Automaton deniedFieldsAutomaton;
|
Automaton deniedFieldsAutomaton;
|
||||||
if (deniedFieldsArray == null || deniedFieldsArray.length == 0) {
|
if (deniedFields == null || deniedFields.length == 0) {
|
||||||
deniedFieldsAutomaton = Automatons.EMPTY;
|
deniedFieldsAutomaton = Automatons.EMPTY;
|
||||||
} else {
|
} else {
|
||||||
deniedFieldsAutomaton = Automatons.patterns(deniedFieldsArray);
|
deniedFieldsAutomaton = Automatons.patterns(deniedFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
grantedFieldsAutomaton = MinimizationOperations.minimize(grantedFieldsAutomaton, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
|
grantedFieldsAutomaton = MinimizationOperations.minimize(grantedFieldsAutomaton, Operations.DEFAULT_MAX_DETERMINIZED_STATES);
|
||||||
|
@ -137,12 +149,12 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
|
|
||||||
if (subsetOf(deniedFieldsAutomaton, grantedFieldsAutomaton) == false) {
|
if (subsetOf(deniedFieldsAutomaton, grantedFieldsAutomaton) == false) {
|
||||||
throw new ElasticsearchSecurityException("Exceptions for field permissions must be a subset of the " +
|
throw new ElasticsearchSecurityException("Exceptions for field permissions must be a subset of the " +
|
||||||
"granted fields but " + Arrays.toString(deniedFieldsArray) + " is not a subset of " +
|
"granted fields but " + Strings.arrayToCommaDelimitedString(deniedFields) + " is not a subset of " +
|
||||||
Arrays.toString(grantedFieldsArray));
|
Strings.arrayToCommaDelimitedString(grantedFields));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((grantedFieldsArray == null || Arrays.asList(grantedFieldsArray).contains(AllFieldMapper.NAME) == false) &&
|
if ((grantedFields == null || Arrays.binarySearch(grantedFields, AllFieldMapper.NAME) < 0) &&
|
||||||
(deniedFieldsArray == null || Arrays.asList(deniedFieldsArray).contains(AllFieldMapper.NAME) == false)) {
|
(deniedFields == null || Arrays.binarySearch(deniedFields, AllFieldMapper.NAME) < 0)) {
|
||||||
// It is not explicitly stated whether _all should be allowed
|
// It is not explicitly stated whether _all should be allowed
|
||||||
// In that case we automatically disable _all, unless all fields would match
|
// In that case we automatically disable _all, unless all fields would match
|
||||||
if (Operations.isTotal(grantedFieldsAutomaton) && Operations.isEmpty(deniedFieldsAutomaton)) {
|
if (Operations.isTotal(grantedFieldsAutomaton) && Operations.isEmpty(deniedFieldsAutomaton)) {
|
||||||
|
@ -156,36 +168,6 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
return grantedFieldsAutomaton;
|
return grantedFieldsAutomaton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
|
||||||
out.writeOptionalStringArray(grantedFieldsArray);
|
|
||||||
out.writeOptionalStringArray(deniedFieldsArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
if (grantedFieldsArray != null || deniedFieldsArray != null) {
|
|
||||||
sb.append(RoleDescriptor.Fields.FIELD_PERMISSIONS).append("=[");
|
|
||||||
if (grantedFieldsArray == null) {
|
|
||||||
sb.append(RoleDescriptor.Fields.GRANT_FIELDS).append("=null");
|
|
||||||
} else {
|
|
||||||
sb.append(RoleDescriptor.Fields.GRANT_FIELDS).append("=[")
|
|
||||||
.append(Strings.arrayToCommaDelimitedString(grantedFieldsArray));
|
|
||||||
sb.append("]");
|
|
||||||
}
|
|
||||||
if (deniedFieldsArray == null) {
|
|
||||||
sb.append(", ").append(RoleDescriptor.Fields.EXCEPT_FIELDS).append("=null");
|
|
||||||
} else {
|
|
||||||
sb.append(", ").append(RoleDescriptor.Fields.EXCEPT_FIELDS).append("=[")
|
|
||||||
.append(Strings.arrayToCommaDelimitedString(deniedFieldsArray));
|
|
||||||
sb.append("]");
|
|
||||||
}
|
|
||||||
sb.append("]");
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this field permission policy allows access to the field and false if not.
|
* Returns true if this field permission policy allows access to the field and false if not.
|
||||||
* fieldName can be a wildcard.
|
* fieldName can be a wildcard.
|
||||||
|
@ -194,14 +176,8 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
return permittedFieldsAutomatonIsTotal || permittedFieldsAutomaton.run(fieldName);
|
return permittedFieldsAutomatonIsTotal || permittedFieldsAutomaton.run(fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
FieldPermissionsDefinition getFieldPermissionsDefinition() {
|
||||||
String[] getGrantedFieldsArray() {
|
return fieldPermissionsDefinition;
|
||||||
return grantedFieldsArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String[] getDeniedFieldsArray() {
|
|
||||||
return deniedFieldsArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return whether field-level security is enabled, ie. whether any field might be filtered out. */
|
/** Return whether field-level security is enabled, ie. whether any field might be filtered out. */
|
||||||
|
@ -218,8 +194,8 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// for testing only
|
// for testing only
|
||||||
CharacterRunAutomaton getIncludeAutomaton() {
|
Automaton getIncludeAutomaton() {
|
||||||
return permittedFieldsAutomaton;
|
return originalAutomaton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -229,16 +205,15 @@ public final class FieldPermissions implements Writeable, Accountable {
|
||||||
|
|
||||||
FieldPermissions that = (FieldPermissions) o;
|
FieldPermissions that = (FieldPermissions) o;
|
||||||
|
|
||||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
if (permittedFieldsAutomatonIsTotal != that.permittedFieldsAutomatonIsTotal) return false;
|
||||||
if (!Arrays.equals(grantedFieldsArray, that.grantedFieldsArray)) return false;
|
return fieldPermissionsDefinition != null ?
|
||||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
fieldPermissionsDefinition.equals(that.fieldPermissionsDefinition) : that.fieldPermissionsDefinition == null;
|
||||||
return Arrays.equals(deniedFieldsArray, that.deniedFieldsArray);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = Arrays.hashCode(grantedFieldsArray);
|
int result = fieldPermissionsDefinition != null ? fieldPermissionsDefinition.hashCode() : 0;
|
||||||
result = 31 * result + Arrays.hashCode(deniedFieldsArray);
|
result = 31 * result + (permittedFieldsAutomatonIsTotal ? 1 : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,19 +5,18 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authz.permission;
|
package org.elasticsearch.xpack.security.authz.permission;
|
||||||
|
|
||||||
|
import org.apache.lucene.util.automaton.Automaton;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.common.Strings;
|
|
||||||
import org.elasticsearch.common.cache.Cache;
|
import org.elasticsearch.common.cache.Cache;
|
||||||
import org.elasticsearch.common.cache.CacheBuilder;
|
import org.elasticsearch.common.cache.CacheBuilder;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Setting.Property;
|
import org.elasticsearch.common.settings.Setting.Property;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition.FieldGrantExcludeGroup;
|
||||||
|
import org.elasticsearch.xpack.security.support.Automatons;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.List;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -34,10 +33,10 @@ public final class FieldPermissionsCache {
|
||||||
|
|
||||||
public static final Setting<Long> CACHE_SIZE_SETTING = Setting.longSetting(
|
public static final Setting<Long> CACHE_SIZE_SETTING = Setting.longSetting(
|
||||||
setting("authz.store.roles.field_permissions.cache.max_size_in_bytes"), 100 * 1024 * 1024, -1L, Property.NodeScope);
|
setting("authz.store.roles.field_permissions.cache.max_size_in_bytes"), 100 * 1024 * 1024, -1L, Property.NodeScope);
|
||||||
private final Cache<Key, FieldPermissions> cache;
|
private final Cache<FieldPermissionsDefinition, FieldPermissions> cache;
|
||||||
|
|
||||||
public FieldPermissionsCache(Settings settings) {
|
public FieldPermissionsCache(Settings settings) {
|
||||||
this.cache = CacheBuilder.<Key, FieldPermissions>builder()
|
this.cache = CacheBuilder.<FieldPermissionsDefinition, FieldPermissions>builder()
|
||||||
.setMaximumWeight(CACHE_SIZE_SETTING.get(settings))
|
.setMaximumWeight(CACHE_SIZE_SETTING.get(settings))
|
||||||
.weigher((key, fieldPermissions) -> fieldPermissions.ramBytesUsed())
|
.weigher((key, fieldPermissions) -> fieldPermissions.ramBytesUsed())
|
||||||
.build();
|
.build();
|
||||||
|
@ -48,36 +47,17 @@ public final class FieldPermissionsCache {
|
||||||
* or if it gets created, the instance will be cached
|
* or if it gets created, the instance will be cached
|
||||||
*/
|
*/
|
||||||
FieldPermissions getFieldPermissions(String[] granted, String[] denied) {
|
FieldPermissions getFieldPermissions(String[] granted, String[] denied) {
|
||||||
final Set<String> grantedSet;
|
return getFieldPermissions(new FieldPermissionsDefinition(granted, denied));
|
||||||
if (granted != null) {
|
|
||||||
grantedSet = new HashSet<>(granted.length);
|
|
||||||
Collections.addAll(grantedSet, granted);
|
|
||||||
} else {
|
|
||||||
grantedSet = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Set<String> deniedSet;
|
|
||||||
if (denied != null) {
|
|
||||||
deniedSet = new HashSet<>(denied.length);
|
|
||||||
Collections.addAll(deniedSet, denied);
|
|
||||||
} else {
|
|
||||||
deniedSet = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getFieldPermissions(grantedSet, deniedSet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a {@link FieldPermissions} instance that corresponds to the granted and denied parameters. The instance may come from the cache
|
* Gets a {@link FieldPermissions} instance that corresponds to the granted and denied parameters. The instance may come from the cache
|
||||||
* or if it gets created, the instance will be cached
|
* or if it gets created, the instance will be cached
|
||||||
*/
|
*/
|
||||||
public FieldPermissions getFieldPermissions(Set<String> granted, Set<String> denied) {
|
public FieldPermissions getFieldPermissions(FieldPermissionsDefinition fieldPermissionsDefinition) {
|
||||||
Key fpKey = new Key(granted == null ? null : Collections.unmodifiableSet(granted),
|
|
||||||
denied == null ? null : Collections.unmodifiableSet(denied));
|
|
||||||
try {
|
try {
|
||||||
return cache.computeIfAbsent(fpKey,
|
return cache.computeIfAbsent(fieldPermissionsDefinition,
|
||||||
(key) -> new FieldPermissions(key.grantedFields == null ? null : key.grantedFields.toArray(Strings.EMPTY_ARRAY),
|
(key) -> new FieldPermissions(key, FieldPermissions.initializePermittedFieldsAutomaton(key)));
|
||||||
key.deniedFields == null ? null : key.deniedFields.toArray(Strings.EMPTY_ARRAY)));
|
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw new ElasticsearchException("unable to compute field permissions", e);
|
throw new ElasticsearchException("unable to compute field permissions", e);
|
||||||
}
|
}
|
||||||
|
@ -92,89 +72,20 @@ public final class FieldPermissionsCache {
|
||||||
.filter(((Predicate<FieldPermissions>) (FieldPermissions::hasFieldLevelSecurity)).negate())
|
.filter(((Predicate<FieldPermissions>) (FieldPermissions::hasFieldLevelSecurity)).negate())
|
||||||
.findFirst();
|
.findFirst();
|
||||||
return allowAllFieldPermissions.orElseGet(() -> {
|
return allowAllFieldPermissions.orElseGet(() -> {
|
||||||
final Set<String> allowedFields;
|
final Set<FieldGrantExcludeGroup> fieldGrantExcludeGroups = fieldPermissionsCollection.stream()
|
||||||
Optional<FieldPermissions> nullAllowedFields = fieldPermissionsCollection.stream()
|
.flatMap(fieldPermission -> fieldPermission.getFieldPermissionsDefinition().getFieldGrantExcludeGroups().stream())
|
||||||
.filter((fieldPermissions) -> fieldPermissions.getGrantedFieldsArray() == null)
|
|
||||||
.findFirst();
|
|
||||||
if (nullAllowedFields.isPresent()) {
|
|
||||||
allowedFields = null;
|
|
||||||
} else {
|
|
||||||
allowedFields = fieldPermissionsCollection.stream()
|
|
||||||
.flatMap(fieldPermissions -> Arrays.stream(fieldPermissions.getGrantedFieldsArray()))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
|
||||||
|
|
||||||
final Set<String> deniedFields = fieldPermissionsCollection.stream()
|
|
||||||
.filter(fieldPermissions -> fieldPermissions.getDeniedFieldsArray() != null)
|
|
||||||
.flatMap(fieldPermissions -> Arrays.stream(fieldPermissions.getDeniedFieldsArray()))
|
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
final FieldPermissionsDefinition combined = new FieldPermissionsDefinition(fieldGrantExcludeGroups);
|
||||||
try {
|
try {
|
||||||
return cache.computeIfAbsent(new Key(allowedFields, deniedFields),
|
return cache.computeIfAbsent(combined, (key) -> {
|
||||||
(key) -> {
|
List<Automaton> automatonList = fieldPermissionsCollection.stream()
|
||||||
final String[] actualDeniedFields = key.deniedFields == null ? null :
|
.map(FieldPermissions::getIncludeAutomaton)
|
||||||
computeDeniedFieldsForPermissions(fieldPermissionsCollection, key.deniedFields);
|
.collect(Collectors.toList());
|
||||||
return new FieldPermissions(key.grantedFields == null ? null : key.grantedFields.toArray(Strings.EMPTY_ARRAY),
|
return new FieldPermissions(key, Automatons.unionAndMinimize(automatonList));
|
||||||
actualDeniedFields);
|
|
||||||
});
|
});
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
throw new ElasticsearchException("unable to compute field permissions", e);
|
throw new ElasticsearchException("unable to compute field permissions", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] computeDeniedFieldsForPermissions(Collection<FieldPermissions> fieldPermissionsCollection,
|
|
||||||
Set<String> allDeniedFields) {
|
|
||||||
Set<String> allowedDeniedFields = new HashSet<>();
|
|
||||||
fieldPermissionsCollection
|
|
||||||
.stream()
|
|
||||||
.filter(fieldPermissions -> fieldPermissions.getDeniedFieldsArray() != null)
|
|
||||||
.forEach((fieldPermissions) -> {
|
|
||||||
String[] deniedFieldsForPermission = fieldPermissions.getDeniedFieldsArray();
|
|
||||||
fieldPermissionsCollection.forEach((fp) -> {
|
|
||||||
if (fp != fieldPermissions) {
|
|
||||||
Arrays.stream(deniedFieldsForPermission).forEach((field) -> {
|
|
||||||
if (fp.grantsAccessTo(field)) {
|
|
||||||
allowedDeniedFields.add(field);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Set<String> difference = Sets.difference(allDeniedFields, allowedDeniedFields);
|
|
||||||
if (difference.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return difference.toArray(Strings.EMPTY_ARRAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class Key {
|
|
||||||
|
|
||||||
private final Set<String> grantedFields;
|
|
||||||
private final Set<String> deniedFields;
|
|
||||||
|
|
||||||
Key(Set<String> grantedFields, Set<String> deniedFields) {
|
|
||||||
this.grantedFields = grantedFields == null ? null : Collections.unmodifiableSet(grantedFields);
|
|
||||||
this.deniedFields = deniedFields == null ? null : Collections.unmodifiableSet(deniedFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof Key)) return false;
|
|
||||||
|
|
||||||
Key key = (Key) o;
|
|
||||||
|
|
||||||
if (grantedFields != null ? !grantedFields.equals(key.grantedFields) : key.grantedFields != null) return false;
|
|
||||||
return deniedFields != null ? deniedFields.equals(key.deniedFields) : key.deniedFields == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = grantedFields != null ? grantedFields.hashCode() : 0;
|
|
||||||
result = 31 * result + (deniedFields != null ? deniedFields.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.xpack.security.authz.permission;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the definition of a {@link FieldPermissions}. Field permissions are defined as a
|
||||||
|
* collections of grant and exclude definitions where the exclude definition must be a subset of
|
||||||
|
* the grant definition.
|
||||||
|
*/
|
||||||
|
public final class FieldPermissionsDefinition {
|
||||||
|
|
||||||
|
private final Set<FieldGrantExcludeGroup> fieldGrantExcludeGroups;
|
||||||
|
|
||||||
|
public FieldPermissionsDefinition(String[] grant, String[] exclude) {
|
||||||
|
this(Collections.singleton(new FieldGrantExcludeGroup(grant, exclude)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldPermissionsDefinition(Set<FieldGrantExcludeGroup> fieldGrantExcludeGroups) {
|
||||||
|
this.fieldGrantExcludeGroups = Collections.unmodifiableSet(fieldGrantExcludeGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<FieldGrantExcludeGroup> getFieldGrantExcludeGroups() {
|
||||||
|
return fieldGrantExcludeGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
FieldPermissionsDefinition that = (FieldPermissionsDefinition) o;
|
||||||
|
|
||||||
|
return fieldGrantExcludeGroups != null ?
|
||||||
|
fieldGrantExcludeGroups.equals(that.fieldGrantExcludeGroups) :
|
||||||
|
that.fieldGrantExcludeGroups == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return fieldGrantExcludeGroups != null ? fieldGrantExcludeGroups.hashCode() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class FieldGrantExcludeGroup {
|
||||||
|
private final String[] grantedFields;
|
||||||
|
private final String[] excludedFields;
|
||||||
|
|
||||||
|
public FieldGrantExcludeGroup(String[] grantedFields, String[] excludedFields) {
|
||||||
|
this.grantedFields = grantedFields;
|
||||||
|
this.excludedFields = excludedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getGrantedFields() {
|
||||||
|
return grantedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getExcludedFields() {
|
||||||
|
return excludedFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
FieldGrantExcludeGroup that = (FieldGrantExcludeGroup) o;
|
||||||
|
|
||||||
|
if (!Arrays.equals(grantedFields, that.grantedFields)) return false;
|
||||||
|
return Arrays.equals(excludedFields, that.excludedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = Arrays.hashCode(grantedFields);
|
||||||
|
result = 31 * result + Arrays.hashCode(excludedFields);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,9 +147,13 @@ public final class Role {
|
||||||
@Nullable FieldPermissionsCache fieldPermissionsCache) {
|
@Nullable FieldPermissionsCache fieldPermissionsCache) {
|
||||||
List<IndicesPermission.Group> list = new ArrayList<>(indicesPrivileges.length);
|
List<IndicesPermission.Group> list = new ArrayList<>(indicesPrivileges.length);
|
||||||
for (RoleDescriptor.IndicesPrivileges privilege : indicesPrivileges) {
|
for (RoleDescriptor.IndicesPrivileges privilege : indicesPrivileges) {
|
||||||
final FieldPermissions fieldPermissions = fieldPermissionsCache != null ?
|
final FieldPermissions fieldPermissions;
|
||||||
fieldPermissionsCache.getFieldPermissions(privilege.getGrantedFields(), privilege.getDeniedFields()) :
|
if (fieldPermissionsCache != null) {
|
||||||
new FieldPermissions(privilege.getGrantedFields(), privilege.getDeniedFields());
|
fieldPermissions = fieldPermissionsCache.getFieldPermissions(privilege.getGrantedFields(), privilege.getDeniedFields());
|
||||||
|
} else {
|
||||||
|
fieldPermissions = new FieldPermissions(
|
||||||
|
new FieldPermissionsDefinition(privilege.getGrantedFields(), privilege.getDeniedFields()));
|
||||||
|
}
|
||||||
final Set<BytesReference> query = privilege.getQuery() == null ? null : Collections.singleton(privilege.getQuery());
|
final Set<BytesReference> query = privilege.getQuery() == null ? null : Collections.singleton(privilege.getQuery());
|
||||||
list.add(new IndicesPermission.Group(IndexPrivilege.get(Sets.newHashSet(privilege.getPrivileges())),
|
list.add(new IndicesPermission.Group(IndexPrivilege.get(Sets.newHashSet(privilege.getPrivileges())),
|
||||||
fieldPermissions,
|
fieldPermissions,
|
||||||
|
|
|
@ -24,6 +24,8 @@ import org.elasticsearch.xpack.common.IteratingActionListener;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition.FieldGrantExcludeGroup;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
|
||||||
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
|
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
|
||||||
|
@ -248,7 +250,7 @@ public class CompositeRolesStore extends AbstractComponent {
|
||||||
.runAs(runAsPrivilege);
|
.runAs(runAsPrivilege);
|
||||||
indicesPrivilegesMap.entrySet().forEach((entry) -> {
|
indicesPrivilegesMap.entrySet().forEach((entry) -> {
|
||||||
MergeableIndicesPrivilege privilege = entry.getValue();
|
MergeableIndicesPrivilege privilege = entry.getValue();
|
||||||
builder.add(fieldPermissionsCache.getFieldPermissions(privilege.grantedFields, privilege.deniedFields), privilege.query,
|
builder.add(fieldPermissionsCache.getFieldPermissions(privilege.fieldPermissionsDefinition), privilege.query,
|
||||||
IndexPrivilege.get(privilege.privileges), privilege.indices.toArray(Strings.EMPTY_ARRAY));
|
IndexPrivilege.get(privilege.privileges), privilege.indices.toArray(Strings.EMPTY_ARRAY));
|
||||||
});
|
});
|
||||||
return builder.build();
|
return builder.build();
|
||||||
|
@ -293,16 +295,14 @@ public class CompositeRolesStore extends AbstractComponent {
|
||||||
private static class MergeableIndicesPrivilege {
|
private static class MergeableIndicesPrivilege {
|
||||||
private Set<String> indices;
|
private Set<String> indices;
|
||||||
private Set<String> privileges;
|
private Set<String> privileges;
|
||||||
private Set<String> grantedFields = null;
|
private FieldPermissionsDefinition fieldPermissionsDefinition;
|
||||||
private Set<String> deniedFields = null;
|
|
||||||
private Set<BytesReference> query = null;
|
private Set<BytesReference> query = null;
|
||||||
|
|
||||||
MergeableIndicesPrivilege(String[] indices, String[] privileges, @Nullable String[] grantedFields, @Nullable String[] deniedFields,
|
MergeableIndicesPrivilege(String[] indices, String[] privileges, @Nullable String[] grantedFields, @Nullable String[] deniedFields,
|
||||||
@Nullable BytesReference query) {
|
@Nullable BytesReference query) {
|
||||||
this.indices = Sets.newHashSet(Objects.requireNonNull(indices));
|
this.indices = Sets.newHashSet(Objects.requireNonNull(indices));
|
||||||
this.privileges = Sets.newHashSet(Objects.requireNonNull(privileges));
|
this.privileges = Sets.newHashSet(Objects.requireNonNull(privileges));
|
||||||
this.grantedFields = grantedFields == null ? null : Sets.newHashSet(grantedFields);
|
this.fieldPermissionsDefinition = new FieldPermissionsDefinition(grantedFields, deniedFields);
|
||||||
this.deniedFields = deniedFields == null ? null : Sets.newHashSet(deniedFields);
|
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
this.query = Sets.newHashSet(query);
|
this.query = Sets.newHashSet(query);
|
||||||
}
|
}
|
||||||
|
@ -310,8 +310,10 @@ public class CompositeRolesStore extends AbstractComponent {
|
||||||
|
|
||||||
void merge(MergeableIndicesPrivilege other) {
|
void merge(MergeableIndicesPrivilege other) {
|
||||||
assert indices.equals(other.indices) : "index names must be equivalent in order to merge";
|
assert indices.equals(other.indices) : "index names must be equivalent in order to merge";
|
||||||
this.grantedFields = combineFieldSets(this.grantedFields, other.grantedFields);
|
Set<FieldGrantExcludeGroup> groups = new HashSet<>();
|
||||||
this.deniedFields = combineFieldSets(this.deniedFields, other.deniedFields);
|
groups.addAll(this.fieldPermissionsDefinition.getFieldGrantExcludeGroups());
|
||||||
|
groups.addAll(other.fieldPermissionsDefinition.getFieldGrantExcludeGroups());
|
||||||
|
this.fieldPermissionsDefinition = new FieldPermissionsDefinition(groups);
|
||||||
this.privileges.addAll(other.privileges);
|
this.privileges.addAll(other.privileges);
|
||||||
|
|
||||||
if (this.query == null || other.query == null) {
|
if (this.query == null || other.query == null) {
|
||||||
|
@ -320,15 +322,5 @@ public class CompositeRolesStore extends AbstractComponent {
|
||||||
this.query.addAll(other.query);
|
this.query.addAll(other.query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<String> combineFieldSets(Set<String> set, Set<String> other) {
|
|
||||||
if (set == null || other == null) {
|
|
||||||
// null = grant all so it trumps others
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
set.addAll(other);
|
|
||||||
return set;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.xpack.security.action.user.PutUserResponse;
|
||||||
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
import org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
|
@ -105,8 +106,8 @@ public class OldSecurityIndexBackwardsCompatibilityTests extends AbstractOldXPac
|
||||||
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
|
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
|
||||||
assertThat(indicesPrivileges.getIndices(), arrayWithSize(2));
|
assertThat(indicesPrivileges.getIndices(), arrayWithSize(2));
|
||||||
assertArrayEquals(new String[] { "index1", "index2" }, indicesPrivileges.getIndices());
|
assertArrayEquals(new String[] { "index1", "index2" }, indicesPrivileges.getIndices());
|
||||||
final FieldPermissions fieldPermissions =
|
final FieldPermissions fieldPermissions = new FieldPermissions(
|
||||||
new FieldPermissions(indicesPrivileges.getGrantedFields(), indicesPrivileges.getDeniedFields());
|
new FieldPermissionsDefinition(indicesPrivileges.getGrantedFields(), indicesPrivileges.getDeniedFields()));
|
||||||
assertTrue(fieldPermissions.grantsAccessTo("title"));
|
assertTrue(fieldPermissions.grantsAccessTo("title"));
|
||||||
assertTrue(fieldPermissions.grantsAccessTo("body"));
|
assertTrue(fieldPermissions.grantsAccessTo("body"));
|
||||||
assertArrayEquals(new String[] { "all" }, indicesPrivileges.getPrivileges());
|
assertArrayEquals(new String[] { "all" }, indicesPrivileges.getPrivileges());
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission;
|
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
|
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
|
||||||
|
@ -50,7 +51,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
Set<BytesReference> query = Collections.singleton(new BytesArray("{}"));
|
Set<BytesReference> query = Collections.singleton(new BytesArray("{}"));
|
||||||
String[] fields = new String[]{"_field"};
|
String[] fields = new String[]{"_field"};
|
||||||
Role role = Role.builder("_role")
|
Role role = Role.builder("_role")
|
||||||
.add(new FieldPermissions(fields, null), query, IndexPrivilege.ALL, "_index").build();
|
.add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, "_index").build();
|
||||||
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
|
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
||||||
|
@ -60,7 +61,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
|
|
||||||
// no document level security:
|
// no document level security:
|
||||||
role = Role.builder("_role")
|
role = Role.builder("_role")
|
||||||
.add(new FieldPermissions(fields, null), null, IndexPrivilege.ALL, "_index").build();
|
.add(new FieldPermissions(fieldPermissionDef(fields, null)), null, IndexPrivilege.ALL, "_index").build();
|
||||||
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
||||||
|
@ -76,7 +77,9 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
|
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
|
||||||
|
|
||||||
// index group associated with an alias:
|
// index group associated with an alias:
|
||||||
role = Role.builder("_role").add(new FieldPermissions(fields, null), query, IndexPrivilege.ALL, "_alias").build();
|
role = Role.builder("_role")
|
||||||
|
.add(new FieldPermissions(fieldPermissionDef(fields, null)), query, IndexPrivilege.ALL, "_alias")
|
||||||
|
.build();
|
||||||
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
|
||||||
|
@ -88,7 +91,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
String[] allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"},
|
String[] allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"},
|
||||||
new String[]{randomAsciiOfLengthBetween(1, 10), "*"});
|
new String[]{randomAsciiOfLengthBetween(1, 10), "*"});
|
||||||
role = Role.builder("_role")
|
role = Role.builder("_role")
|
||||||
.add(new FieldPermissions(allFields, null), query, IndexPrivilege.ALL, "_alias").build();
|
.add(new FieldPermissions(fieldPermissionDef(allFields, null)), query, IndexPrivilege.ALL, "_alias").build();
|
||||||
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertFalse(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
|
assertFalse(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
|
||||||
|
@ -139,8 +142,8 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
|
|
||||||
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
|
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
|
||||||
IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, "a1");
|
IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, "a1");
|
||||||
IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(null, new
|
IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.ALL,
|
||||||
String[]{"denied_field"}), null, "a1");
|
new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, "a1");
|
||||||
IndicesPermission core = new IndicesPermission(group1, group2);
|
IndicesPermission core = new IndicesPermission(group1, group2);
|
||||||
Map<String, IndicesAccessControl.IndexAccessControl> authzMap =
|
Map<String, IndicesAccessControl.IndexAccessControl> authzMap =
|
||||||
core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData, fieldPermissionsCache);
|
core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData, fieldPermissionsCache);
|
||||||
|
@ -154,12 +157,12 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
|
|
||||||
// test with two indices
|
// test with two indices
|
||||||
group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, "a1");
|
group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, "a1");
|
||||||
group2 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(null, new
|
group2 = new IndicesPermission.Group(IndexPrivilege.ALL,
|
||||||
String[]{"denied_field"}), null, "a1");
|
new FieldPermissions(fieldPermissionDef(null, new String[]{"denied_field"})), null, "a1");
|
||||||
IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(new String[]{"*_field"}
|
IndicesPermission.Group group3 = new IndicesPermission.Group(IndexPrivilege.ALL,
|
||||||
, new String[]{"denied_field"}), null, "a2");
|
new FieldPermissions(fieldPermissionDef(new String[]{"*_field"}, new String[]{"denied_field"})), null, "a2");
|
||||||
IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(new String[]{"*_field2"}
|
IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL,
|
||||||
, new String[]{"denied_field2"}), null, "a2");
|
new FieldPermissions(fieldPermissionDef(new String[]{"*_field2"}, new String[]{"denied_field2"})), null, "a2");
|
||||||
core = new IndicesPermission(group1, group2, group3, group4);
|
core = new IndicesPermission(group1, group2, group3, group4);
|
||||||
authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), metaData, fieldPermissionsCache);
|
authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), metaData, fieldPermissionsCache);
|
||||||
assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity());
|
assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity());
|
||||||
|
@ -172,4 +175,8 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
assertTrue(core.check(SearchAction.NAME));
|
assertTrue(core.check(SearchAction.NAME));
|
||||||
assertFalse(core.check("unknown"));
|
assertFalse(core.check("unknown"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) {
|
||||||
|
return new FieldPermissionsDefinition(granted, denied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.apache.lucene.search.Weight;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
@ -52,56 +53,60 @@ public class OptOutQueryCacheTests extends ESTestCase {
|
||||||
|
|
||||||
// whenever the allowed fields match the fields in the query and we do not deny access to any fields we allow caching.
|
// whenever the allowed fields match the fields in the query and we do not deny access to any fields we allow caching.
|
||||||
IndicesAccessControl.IndexAccessControl permissions = new IndicesAccessControl.IndexAccessControl(true,
|
IndicesAccessControl.IndexAccessControl permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"foo", "no"}, null), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"foo", "no"}, null)), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"foo", "no"}, new String[]{}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"foo", "no"}, new String[]{})), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"*"}, new String[]{}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"*"}, new String[]{})), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"*"}, null), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"*"}, null)), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"*"}, new String[]{"oof"}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"*"}, new String[]{"oof"})), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"f*", "n*"}, new String[]{}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"f*", "n*"}, new String[]{})), new HashSet<>());
|
||||||
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertTrue(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
// check we don't cache if a field is not allowed
|
// check we don't cache if a field is not allowed
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"foo"}, null), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"foo"}, null)), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"a*"}, new String[]{"aa"}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"a*"}, new String[]{"aa"})), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(null, new String[]{"no"}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(null, new String[]{"no"})), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(null, new String[]{"*"}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(null, new String[]{"*"})), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{"foo", "no"}, new String[]{"no"}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{"foo", "no"}, new String[]{"no"})), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{}, new String[]{}), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{}, new String[]{})), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
|
|
||||||
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
permissions = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{}, null), new HashSet<>());
|
new FieldPermissions(fieldPermissionDef(new String[]{}, null)), new HashSet<>());
|
||||||
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
assertFalse(OptOutQueryCache.cachingIsSafe(weight, permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) {
|
||||||
|
return new FieldPermissionsDefinition(granted, denied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.IndexSettingsModule;
|
import org.elasticsearch.test.IndexSettingsModule;
|
||||||
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
import org.elasticsearch.xpack.security.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -159,7 +160,7 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
@Override
|
@Override
|
||||||
protected IndicesAccessControl getIndicesAccessControl() {
|
protected IndicesAccessControl getIndicesAccessControl() {
|
||||||
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
|
||||||
new FieldPermissions(new String[]{}, null), null);
|
new FieldPermissions(fieldPermissionDef(new String[]{}, null)), null);
|
||||||
return new IndicesAccessControl(true, singletonMap("_index", indexAccessControl));
|
return new IndicesAccessControl(true, singletonMap("_index", indexAccessControl));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -206,24 +207,24 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
expected.add("field1_a");
|
expected.add("field1_a");
|
||||||
expected.add("field1_b");
|
expected.add("field1_b");
|
||||||
expected.add("field1_c");
|
expected.add("field1_c");
|
||||||
assertResolved(new FieldPermissions(new String[] {"field1*"}, null), expected, "field", "field2");
|
assertResolved(new FieldPermissions(fieldPermissionDef(new String[] {"field1*"}, null)), expected, "field", "field2");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDotNotion() throws Exception {
|
public void testDotNotion() throws Exception {
|
||||||
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("foo.bar");
|
expected.add("foo.bar");
|
||||||
assertResolved(new FieldPermissions(new String[] {"foo.bar"}, null), expected, "foo", "foo.baz", "bar.foo");
|
assertResolved(new FieldPermissions(fieldPermissionDef(new String[] {"foo.bar"}, null)), expected, "foo", "foo.baz", "bar.foo");
|
||||||
|
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("foo.bar");
|
expected.add("foo.bar");
|
||||||
assertResolved(new FieldPermissions(new String[] {"foo.*"}, null), expected, "foo", "bar");
|
assertResolved(new FieldPermissions(fieldPermissionDef(new String[] {"foo.*"}, null)), expected, "foo", "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testParentChild() throws Exception {
|
public void testParentChild() throws Exception {
|
||||||
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add(ParentFieldMapper.joinField("parent1"));
|
expected.add(ParentFieldMapper.joinField("parent1"));
|
||||||
expected.add("foo");
|
expected.add("foo");
|
||||||
assertResolved(new FieldPermissions(new String[] {"foo"}, null), expected, "bar");
|
assertResolved(new FieldPermissions(fieldPermissionDef(new String[] {"foo"}, null)), expected, "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDelegateSimilarity() throws Exception {
|
public void testDelegateSimilarity() throws Exception {
|
||||||
|
@ -343,12 +344,12 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
String[] deniedFields;
|
String[] deniedFields;
|
||||||
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
Set<String> expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
// Presence of fields in a role with an empty array implies access to no fields except the meta fields
|
// Presence of fields in a role with an empty array implies access to no fields except the meta fields
|
||||||
assertResolved(new FieldPermissions(grantedFields, randomBoolean() ? null : new String[]{}),
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, randomBoolean() ? null : new String[]{})),
|
||||||
expected, "foo", "bar");
|
expected, "foo", "bar");
|
||||||
|
|
||||||
// make sure meta fields cannot be denied access to
|
// make sure meta fields cannot be denied access to
|
||||||
deniedFields = META_FIELDS_WITHOUT_ALL.toArray(new String[0]);
|
deniedFields = META_FIELDS_WITHOUT_ALL.toArray(new String[0]);
|
||||||
assertResolved(new FieldPermissions(null, deniedFields),
|
assertResolved(new FieldPermissions(fieldPermissionDef(null, deniedFields)),
|
||||||
new HashSet<>(Arrays.asList("foo", "bar", "_some_plugin_meta_field")));
|
new HashSet<>(Arrays.asList("foo", "bar", "_some_plugin_meta_field")));
|
||||||
|
|
||||||
// check we can add all fields with *
|
// check we can add all fields with *
|
||||||
|
@ -356,50 +357,50 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add(AllFieldMapper.NAME);
|
expected.add(AllFieldMapper.NAME);
|
||||||
expected.add("foo");
|
expected.add("foo");
|
||||||
assertResolved(new FieldPermissions(grantedFields, randomBoolean() ? null : new String[]{}), expected);
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, randomBoolean() ? null : new String[]{})), expected);
|
||||||
|
|
||||||
// same with null
|
// same with null
|
||||||
grantedFields = null;
|
grantedFields = null;
|
||||||
assertResolved(new FieldPermissions(grantedFields, randomBoolean() ? null : new String[]{}), expected);
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, randomBoolean() ? null : new String[]{})), expected);
|
||||||
|
|
||||||
// check we remove only excluded fields
|
// check we remove only excluded fields
|
||||||
grantedFields = new String[]{"*"};
|
grantedFields = new String[]{"*"};
|
||||||
deniedFields = new String[]{"xfield"};
|
deniedFields = new String[]{"xfield"};
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("foo");
|
expected.add("foo");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "xfield", "_all");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "xfield", "_all");
|
||||||
|
|
||||||
// same with null
|
// same with null
|
||||||
grantedFields = null;
|
grantedFields = null;
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "xfield", "_all");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "xfield", "_all");
|
||||||
|
|
||||||
// some other checks
|
// some other checks
|
||||||
grantedFields = new String[]{"field*"};
|
grantedFields = new String[]{"field*"};
|
||||||
deniedFields = new String[]{"field1", "field2"};
|
deniedFields = new String[]{"field1", "field2"};
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("field3");
|
expected.add("field3");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "field1", "field2", "_all");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "field1", "field2", "_all");
|
||||||
|
|
||||||
grantedFields = new String[]{"field1", "field2"};
|
grantedFields = new String[]{"field1", "field2"};
|
||||||
deniedFields = new String[]{"field2"};
|
deniedFields = new String[]{"field2"};
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("field1");
|
expected.add("field1");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "field1", "field2", "_all");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "field1", "field2", "_all");
|
||||||
|
|
||||||
grantedFields = new String[]{"field*"};
|
grantedFields = new String[]{"field*"};
|
||||||
deniedFields = new String[]{"field2"};
|
deniedFields = new String[]{"field2"};
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("field1");
|
expected.add("field1");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "field2", "_all");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "field2", "_all");
|
||||||
|
|
||||||
deniedFields = new String[]{"field*"};
|
deniedFields = new String[]{"field*"};
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields),
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)),
|
||||||
META_FIELDS_WITHOUT_ALL, "field1", "field2");
|
META_FIELDS_WITHOUT_ALL, "field1", "field2");
|
||||||
|
|
||||||
// empty array for allowed fields always means no field is allowed
|
// empty array for allowed fields always means no field is allowed
|
||||||
grantedFields = new String[]{};
|
grantedFields = new String[]{};
|
||||||
deniedFields = new String[]{};
|
deniedFields = new String[]{};
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields),
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)),
|
||||||
META_FIELDS_WITHOUT_ALL, "field1", "field2");
|
META_FIELDS_WITHOUT_ALL, "field1", "field2");
|
||||||
|
|
||||||
// make sure all field can be explicitly allowed
|
// make sure all field can be explicitly allowed
|
||||||
|
@ -408,14 +409,14 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("_all");
|
expected.add("_all");
|
||||||
expected.add("field1");
|
expected.add("field1");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected);
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected);
|
||||||
|
|
||||||
// make sure all field can be explicitly allowed
|
// make sure all field can be explicitly allowed
|
||||||
grantedFields = new String[]{"_all"};
|
grantedFields = new String[]{"_all"};
|
||||||
deniedFields = randomBoolean() ? null : new String[]{};
|
deniedFields = randomBoolean() ? null : new String[]{};
|
||||||
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
expected = new HashSet<>(META_FIELDS_WITHOUT_ALL);
|
||||||
expected.add("_all");
|
expected.add("_all");
|
||||||
assertResolved(new FieldPermissions(grantedFields, deniedFields), expected, "field1", "_source");
|
assertResolved(new FieldPermissions(fieldPermissionDef(grantedFields, deniedFields)), expected, "field1", "_source");
|
||||||
}
|
}
|
||||||
|
|
||||||
private SparseFixedBitSet query(LeafReaderContext leaf, String field, String value) throws IOException {
|
private SparseFixedBitSet query(LeafReaderContext leaf, String field, String value) throws IOException {
|
||||||
|
@ -693,4 +694,8 @@ public class SecurityIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
() -> SecurityIndexSearcherWrapper.failIfQueryUsesClient(scriptService, queryBuilder2, context));
|
() -> SecurityIndexSearcherWrapper.failIfQueryUsesClient(scriptService, queryBuilder2, context));
|
||||||
assertThat(e.getMessage(), equalTo("role queries are not allowed to execute additional requests"));
|
assertThat(e.getMessage(), equalTo("role queries are not allowed to execute additional requests"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) {
|
||||||
|
return new FieldPermissionsDefinition(granted, denied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,10 @@ package org.elasticsearch.xpack.security.authz.permission;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class FieldPermissionsCacheTests extends ESTestCase {
|
public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
|
|
||||||
|
@ -33,9 +36,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
String[] denied1 = new String[]{allowedPrefix1 + "a"};
|
String[] denied1 = new String[]{allowedPrefix1 + "a"};
|
||||||
String[] denied2 = new String[]{allowedPrefix2 + "a"};
|
String[] denied2 = new String[]{allowedPrefix2 + "a"};
|
||||||
FieldPermissions fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
FieldPermissions fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
FieldPermissions fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
FieldPermissions fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
FieldPermissions mergedFieldPermissions =
|
FieldPermissions mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix1 + "b"));
|
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix1 + "b"));
|
||||||
|
@ -48,9 +51,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
denied1 = new String[]{allowed1[0] + "a", allowed1[1] + "a"};
|
denied1 = new String[]{allowed1[0] + "a", allowed1[1] + "a"};
|
||||||
denied2 = null;
|
denied2 = null;
|
||||||
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
mergedFieldPermissions =
|
mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
assertFalse(mergedFieldPermissions.hasFieldLevelSecurity());
|
assertFalse(mergedFieldPermissions.hasFieldLevelSecurity());
|
||||||
|
@ -60,9 +63,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
denied1 = new String[]{};
|
denied1 = new String[]{};
|
||||||
denied2 = new String[]{allowed2[0] + "a", allowed2[1] + "a"};
|
denied2 = new String[]{allowed2[0] + "a", allowed2[1] + "a"};
|
||||||
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
mergedFieldPermissions =
|
mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
for (String field : allowed2) {
|
for (String field : allowed2) {
|
||||||
|
@ -77,9 +80,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
denied1 = new String[]{"a"};
|
denied1 = new String[]{"a"};
|
||||||
denied2 = new String[]{"b"};
|
denied2 = new String[]{"b"};
|
||||||
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
mergedFieldPermissions =
|
mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
|
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
|
||||||
|
@ -91,9 +94,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
denied1 = null;
|
denied1 = null;
|
||||||
denied2 = null;
|
denied2 = null;
|
||||||
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
mergedFieldPermissions =
|
mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
assertTrue(fieldPermissions1.grantsAccessTo("_all"));
|
assertTrue(fieldPermissions1.grantsAccessTo("_all"));
|
||||||
|
@ -105,9 +108,9 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
denied1 = new String[] { "aa*" };
|
denied1 = new String[] { "aa*" };
|
||||||
denied2 = null;
|
denied2 = null;
|
||||||
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
|
||||||
new FieldPermissions(allowed1, denied1);
|
new FieldPermissions(fieldPermissionDef(allowed1, denied1));
|
||||||
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
|
||||||
new FieldPermissions(allowed2, denied2);
|
new FieldPermissions(fieldPermissionDef(allowed2, denied2));
|
||||||
mergedFieldPermissions =
|
mergedFieldPermissions =
|
||||||
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
|
||||||
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
|
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
|
||||||
|
@ -116,4 +119,24 @@ public class FieldPermissionsCacheTests extends ESTestCase {
|
||||||
assertFalse(mergedFieldPermissions.grantsAccessTo("aa1"));
|
assertFalse(mergedFieldPermissions.grantsAccessTo("aa1"));
|
||||||
assertTrue(mergedFieldPermissions.grantsAccessTo("a1"));
|
assertTrue(mergedFieldPermissions.grantsAccessTo("a1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testNonFlsAndFlsMerging() {
|
||||||
|
List<FieldPermissions> permissionsList = new ArrayList<>();
|
||||||
|
permissionsList.add(new FieldPermissions(fieldPermissionDef(new String[] {"field1"}, null)));
|
||||||
|
permissionsList.add(new FieldPermissions(fieldPermissionDef(new String[] {"field2", "query*"}, null)));
|
||||||
|
permissionsList.add(new FieldPermissions(fieldPermissionDef(new String[] {"field1", "field2"}, null)));
|
||||||
|
permissionsList.add(new FieldPermissions(fieldPermissionDef(new String[] {}, null)));
|
||||||
|
permissionsList.add(new FieldPermissions(fieldPermissionDef(null, null)));
|
||||||
|
|
||||||
|
FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY);
|
||||||
|
for (int i = 0; i < scaledRandomIntBetween(1, 12); i++) {
|
||||||
|
Collections.shuffle(permissionsList, random());
|
||||||
|
FieldPermissions result = cache.getFieldPermissions(permissionsList);
|
||||||
|
assertFalse(result.hasFieldLevelSecurity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldPermissionsDefinition fieldPermissionDef(String[] granted, String[] denied) {
|
||||||
|
return new FieldPermissionsDefinition(granted, denied);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,13 +7,10 @@ package org.elasticsearch.xpack.security.authz.permission;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
@ -21,7 +18,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
public class FieldPermissionTests extends ESTestCase {
|
public class FieldPermissionsTests extends ESTestCase {
|
||||||
|
|
||||||
public void testParseFieldPermissions() throws Exception {
|
public void testParseFieldPermissions() throws Exception {
|
||||||
String q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
String q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
|
@ -29,9 +26,12 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
"\"grant\": [\"f1\", \"f2\", \"f3\", \"f4\"]," +
|
"\"grant\": [\"f1\", \"f2\", \"f3\", \"f4\"]," +
|
||||||
"\"except\": [\"f3\",\"f4\"]" +
|
"\"except\": [\"f3\",\"f4\"]" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
RoleDescriptor rd =
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" });
|
RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" });
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(),
|
||||||
|
new String[] { "f1", "f2", "f3", "f4" });
|
||||||
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(),
|
||||||
|
new String[] { "f3", "f4" });
|
||||||
|
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
"\"field_security\": {" +
|
"\"field_security\": {" +
|
||||||
|
@ -39,15 +39,18 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
"\"grant\": [\"f1\", \"f2\", \"f3\", \"f4\"]" +
|
"\"grant\": [\"f1\", \"f2\", \"f3\", \"f4\"]" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" });
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(),
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" });
|
new String[] { "f1", "f2", "f3", "f4" });
|
||||||
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(),
|
||||||
|
new String[] { "f3", "f4" });
|
||||||
|
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
"\"field_security\": {" +
|
"\"field_security\": {" +
|
||||||
"\"grant\": [\"f1\", \"f2\"]" +
|
"\"grant\": [\"f1\", \"f2\"]" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
rd = RoleDescriptor.parse("test", new BytesArray(q), false, XContentType.JSON);
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2" });
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(),
|
||||||
|
new String[] { "f1", "f2" });
|
||||||
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
||||||
|
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
|
@ -67,48 +70,58 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {});
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {});
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] {});
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] {});
|
||||||
|
|
||||||
final String exceptWithoutGrant = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
final String exceptWithoutGrant = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\":" +
|
||||||
"\"field_security\": {" +
|
" [\"p3\"], \"field_security\": {" +
|
||||||
"\"except\": [\"f1\"]" +
|
"\"except\": [\"f1\"]" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> RoleDescriptor.parse("test",
|
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
|
||||||
new BytesArray(exceptWithoutGrant), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(exceptWithoutGrant), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. field_security requires " +
|
XContentType.JSON));
|
||||||
"grant if except is given"));
|
assertThat(e.getDetailedMessage(),
|
||||||
|
containsString("failed to parse indices privileges for role [test]. field_security"
|
||||||
|
+ " requires grant if except is given"));
|
||||||
|
|
||||||
final String grantNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
final String grantNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"]," +
|
||||||
"\"field_security\": {" +
|
" \"field_security\": {" +
|
||||||
"\"grant\": null" +
|
"\"grant\": null" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(grantNull), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(grantNull), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. grant must not be null."));
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for" +
|
||||||
|
" role [test]. grant must not be null."));
|
||||||
|
|
||||||
final String exceptNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
final String exceptNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": " +
|
||||||
"\"field_security\": {" +
|
"[\"p3\"], \"field_security\": {" +
|
||||||
"\"grant\": [\"*\"]," +
|
"\"grant\": [\"*\"]," +
|
||||||
"\"except\": null" +
|
"\"except\": null" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(exceptNull), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(exceptNull), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. except must not be null."));
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(),
|
||||||
|
containsString("failed to parse indices privileges for role [test]. except must" +
|
||||||
|
" not be null."));
|
||||||
|
|
||||||
final String exceptGrantNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
final String exceptGrantNull = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": " +
|
||||||
"\"field_security\": {" +
|
"[\"p3\"], \"field_security\": {" +
|
||||||
"\"grant\": null," +
|
"\"grant\": null," +
|
||||||
"\"except\": null" +
|
"\"except\": null" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(exceptGrantNull), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(exceptGrantNull), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. grant must not be null."));
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges " +
|
||||||
|
"for role [test]. grant must not be null."));
|
||||||
|
|
||||||
final String bothFieldsMissing = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
final String bothFieldsMissing = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": " +
|
||||||
"\"field_security\": {" +
|
"[\"p3\"], \"field_security\": {" +
|
||||||
"}}]}";
|
"}}]}";
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(bothFieldsMissing), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(bothFieldsMissing), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges for role [test]. \"field_security\" " +
|
XContentType.JSON));
|
||||||
"must not be empty."));
|
assertThat(e.getDetailedMessage(), containsString("failed to parse indices privileges " +
|
||||||
|
"for role [test]. \"field_security\" must not be empty."));
|
||||||
|
|
||||||
// try with two indices and mix order a little
|
// try with two indices and mix order a little
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
|
@ -132,14 +145,18 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
String q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
String q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
"\"fields\": [\"f1\", \"f2\"]" +
|
"\"fields\": [\"f1\", \"f2\"]" +
|
||||||
"}]}";
|
"}]}";
|
||||||
RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), true, XContentType.JSON);
|
RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), true,
|
||||||
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[]{"f1", "f2"});
|
XContentType.JSON);
|
||||||
|
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(),
|
||||||
|
new String[]{"f1", "f2"});
|
||||||
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
||||||
|
|
||||||
final String failingQuery = q;
|
final String failingQuery = q;
|
||||||
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
|
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has changed for field permissions in role [test]" +
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has " +
|
||||||
|
"changed for field permissions in role [test]" +
|
||||||
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
||||||
|
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
|
@ -150,8 +167,10 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
||||||
final String failingQuery2 = q;
|
final String failingQuery2 = q;
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery2), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery2), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has changed for field permissions in role [test]" +
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has " +
|
||||||
|
"changed for field permissions in role [test]" +
|
||||||
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
||||||
|
|
||||||
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
|
||||||
|
@ -162,28 +181,17 @@ public class FieldPermissionTests extends ESTestCase {
|
||||||
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
|
||||||
final String failingQuery3 = q;
|
final String failingQuery3 = q;
|
||||||
e = expectThrows(ElasticsearchParseException.class,
|
e = expectThrows(ElasticsearchParseException.class,
|
||||||
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery3), false, XContentType.JSON));
|
() -> RoleDescriptor.parse("test", new BytesArray(failingQuery3), false,
|
||||||
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has changed for field permissions in role [test]" +
|
XContentType.JSON));
|
||||||
|
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has " +
|
||||||
|
"changed for field permissions in role [test]" +
|
||||||
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldPermissionsStreaming() throws IOException {
|
|
||||||
BytesStreamOutput out = new BytesStreamOutput();
|
|
||||||
String[] allowed = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
|
|
||||||
String[] denied = new String[]{allowed[0] + randomAsciiOfLength(5), allowed[1] + randomAsciiOfLength(5),
|
|
||||||
allowed[2] + randomAsciiOfLength(5)};
|
|
||||||
FieldPermissions fieldPermissions = new FieldPermissions(allowed, denied);
|
|
||||||
out.writeOptionalWriteable(fieldPermissions);
|
|
||||||
out.close();
|
|
||||||
StreamInput in = out.bytes().streamInput();
|
|
||||||
FieldPermissions readFieldPermissions = in.readOptionalWriteable(FieldPermissions::new);
|
|
||||||
// order should be preserved in any case
|
|
||||||
assertEquals(readFieldPermissions, fieldPermissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFieldPermissionsHashCodeThreadSafe() throws Exception {
|
public void testFieldPermissionsHashCodeThreadSafe() throws Exception {
|
||||||
final int numThreads = scaledRandomIntBetween(4, 16);
|
final int numThreads = scaledRandomIntBetween(4, 16);
|
||||||
final FieldPermissions fieldPermissions = new FieldPermissions(new String[] { "*" }, new String[] { "foo" });
|
final FieldPermissions fieldPermissions =new FieldPermissions(
|
||||||
|
new FieldPermissionsDefinition(new String[] { "*" }, new String[] { "foo" }));
|
||||||
final CountDownLatch latch = new CountDownLatch(numThreads + 1);
|
final CountDownLatch latch = new CountDownLatch(numThreads + 1);
|
||||||
final AtomicReferenceArray<Integer> hashCodes = new AtomicReferenceArray<>(numThreads);
|
final AtomicReferenceArray<Integer> hashCodes = new AtomicReferenceArray<>(numThreads);
|
||||||
List<Thread> threads = new ArrayList<>(numThreads);
|
List<Thread> threads = new ArrayList<>(numThreads);
|
|
@ -5,8 +5,11 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.authz.store;
|
package org.elasticsearch.xpack.security.authz.store;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.PlainActionFuture;
|
import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
|
@ -16,6 +19,7 @@ import org.elasticsearch.license.TestUtils.UpdatableLicenseState;
|
||||||
import org.elasticsearch.license.XPackLicenseState;
|
import org.elasticsearch.license.XPackLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
|
||||||
|
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
|
||||||
|
@ -24,6 +28,7 @@ import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
@ -279,6 +284,45 @@ public class CompositeRolesStoreTests extends ESTestCase {
|
||||||
verifyNoMoreInteractions(inMemoryProvider1, inMemoryProvider2);
|
verifyNoMoreInteractions(inMemoryProvider1, inMemoryProvider2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test is a direct result of a issue where field level security permissions were not
|
||||||
|
* being merged correctly. The improper merging resulted in an allow all result when merging
|
||||||
|
* permissions from different roles instead of properly creating a union of their languages
|
||||||
|
*/
|
||||||
|
public void testMergingRolesWithFls() {
|
||||||
|
RoleDescriptor flsRole = new RoleDescriptor("fls", null, new IndicesPrivileges[] {
|
||||||
|
IndicesPrivileges.builder()
|
||||||
|
.grantedFields("*")
|
||||||
|
.deniedFields("L1.*", "L2.*")
|
||||||
|
.indices("*")
|
||||||
|
.privileges("read")
|
||||||
|
.query("{ \"match\": {\"eventType.typeCode\": \"foo\"} }")
|
||||||
|
.build()
|
||||||
|
}, null);
|
||||||
|
RoleDescriptor addsL1Fields = new RoleDescriptor("dls", null, new IndicesPrivileges[] {
|
||||||
|
IndicesPrivileges.builder()
|
||||||
|
.indices("*")
|
||||||
|
.grantedFields("L1.*")
|
||||||
|
.privileges("read")
|
||||||
|
.query("{ \"match\": {\"eventType.typeCode\": \"foo\"} }")
|
||||||
|
.build()
|
||||||
|
}, null);
|
||||||
|
FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY);
|
||||||
|
Role role = CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(flsRole, addsL1Fields), cache);
|
||||||
|
|
||||||
|
MetaData metaData = MetaData.builder()
|
||||||
|
.put(new IndexMetaData.Builder("test")
|
||||||
|
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
|
||||||
|
.numberOfShards(1).numberOfReplicas(0).build(), true)
|
||||||
|
.build();
|
||||||
|
Map<String, IndicesAccessControl.IndexAccessControl> acls =
|
||||||
|
role.indices().authorize("indices:data/read/search", Collections.singleton("test"), metaData, cache);
|
||||||
|
assertFalse(acls.isEmpty());
|
||||||
|
assertTrue(acls.get("test").getFieldPermissions().grantsAccessTo("L1.foo"));
|
||||||
|
assertFalse(acls.get("test").getFieldPermissions().grantsAccessTo("L2.foo"));
|
||||||
|
assertTrue(acls.get("test").getFieldPermissions().grantsAccessTo("L3.foo"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testCustomRolesProviderFailures() throws Exception {
|
public void testCustomRolesProviderFailures() throws Exception {
|
||||||
final FileRolesStore fileRolesStore = mock(FileRolesStore.class);
|
final FileRolesStore fileRolesStore = mock(FileRolesStore.class);
|
||||||
when(fileRolesStore.roleDescriptors(anySetOf(String.class))).thenReturn(Collections.emptySet());
|
when(fileRolesStore.roleDescriptors(anySetOf(String.class))).thenReturn(Collections.emptySet());
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.xpack.security.authc.esnative.ESNativeRealmMigrateTool;
|
||||||
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
import org.elasticsearch.xpack.security.authc.support.SecuredString;
|
||||||
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
|
||||||
|
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsDefinition;
|
||||||
import org.elasticsearch.xpack.security.client.SecurityClient;
|
import org.elasticsearch.xpack.security.client.SecurityClient;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
@ -93,7 +94,8 @@ public class MigrateToolIT extends MigrateToolTestCase {
|
||||||
RoleDescriptor.IndicesPrivileges[] ips = rd.getIndicesPrivileges();
|
RoleDescriptor.IndicesPrivileges[] ips = rd.getIndicesPrivileges();
|
||||||
assertEquals(ips.length, 2);
|
assertEquals(ips.length, 2);
|
||||||
for (RoleDescriptor.IndicesPrivileges ip : ips) {
|
for (RoleDescriptor.IndicesPrivileges ip : ips) {
|
||||||
final FieldPermissions fieldPermissions = new FieldPermissions(ip.getGrantedFields(), ip.getDeniedFields());
|
final FieldPermissions fieldPermissions = new FieldPermissions(
|
||||||
|
new FieldPermissionsDefinition(ip.getGrantedFields(), ip.getDeniedFields()));
|
||||||
if (Arrays.equals(ip.getIndices(), new String[]{"index1", "index2"})) {
|
if (Arrays.equals(ip.getIndices(), new String[]{"index1", "index2"})) {
|
||||||
assertArrayEquals(ip.getPrivileges(), new String[]{"read", "write", "create_index", "indices:admin/refresh"});
|
assertArrayEquals(ip.getPrivileges(), new String[]{"read", "write", "create_index", "indices:admin/refresh"});
|
||||||
assertTrue(fieldPermissions.hasFieldLevelSecurity());
|
assertTrue(fieldPermissions.hasFieldLevelSecurity());
|
||||||
|
|
Loading…
Reference in New Issue