[Security] Add DEBUG logging on role resolution (elastic/x-pack-elasticsearch#3138)

This change adds some debug and trace logging when we look up role names, to explain how each role was resolved.

At the moment we have very little insight into how roles are being resolved which can make it difficult to diagnose some issues.

Original commit: elastic/x-pack-elasticsearch@1b3c246186
This commit is contained in:
Tim Vernum 2017-11-30 21:34:07 +10:00 committed by GitHub
parent a5fe074b5c
commit 4262b29188
2 changed files with 58 additions and 32 deletions

View File

@ -5,35 +5,8 @@
*/ */
package org.elasticsearch.xpack.security.authz.store; package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.health.ClusterIndexHealth;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.concurrent.ReleasableLock;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.common.IteratingActionListener;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
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.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -48,6 +21,35 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.cluster.health.ClusterHealthStatus;
import org.elasticsearch.cluster.health.ClusterIndexHealth;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.concurrent.ReleasableLock;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.xpack.common.IteratingActionListener;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
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.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import static org.elasticsearch.xpack.security.Security.setting; import static org.elasticsearch.xpack.security.Security.setting;
/** /**
@ -143,14 +145,21 @@ public class CompositeRolesStore extends AbstractComponent {
} }
private void roleDescriptors(Set<String> roleNames, ActionListener<Set<RoleDescriptor>> roleDescriptorActionListener) { private void roleDescriptors(Set<String> roleNames, ActionListener<Set<RoleDescriptor>> roleDescriptorActionListener) {
final Set<String> filteredRoleNames = final Set<String> filteredRoleNames = roleNames.stream().filter((s) -> {
roleNames.stream().filter((s) -> negativeLookupCache.contains(s) == false).collect(Collectors.toSet()); if (negativeLookupCache.contains(s)) {
logger.debug("Requested role [{}] does not exist (cached)", s);
return false;
} else {
return true;
}
}).collect(Collectors.toSet());
final Set<RoleDescriptor> builtInRoleDescriptors = getBuiltInRoleDescriptors(filteredRoleNames); final Set<RoleDescriptor> builtInRoleDescriptors = getBuiltInRoleDescriptors(filteredRoleNames);
Set<String> remainingRoleNames = difference(filteredRoleNames, builtInRoleDescriptors); Set<String> remainingRoleNames = difference(filteredRoleNames, builtInRoleDescriptors);
if (remainingRoleNames.isEmpty()) { if (remainingRoleNames.isEmpty()) {
roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors)); roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors));
} else { } else {
nativeRolesStore.getRoleDescriptors(remainingRoleNames.toArray(Strings.EMPTY_ARRAY), ActionListener.wrap((descriptors) -> { nativeRolesStore.getRoleDescriptors(remainingRoleNames.toArray(Strings.EMPTY_ARRAY), ActionListener.wrap((descriptors) -> {
logger.debug(() -> new ParameterizedMessage("Roles [{}] were resolved from the native index store", names(descriptors)));
builtInRoleDescriptors.addAll(descriptors); builtInRoleDescriptors.addAll(descriptors);
callCustomRoleProvidersIfEnabled(builtInRoleDescriptors, filteredRoleNames, roleDescriptorActionListener); callCustomRoleProvidersIfEnabled(builtInRoleDescriptors, filteredRoleNames, roleDescriptorActionListener);
}, e -> { }, e -> {
@ -169,6 +178,8 @@ public class CompositeRolesStore extends AbstractComponent {
new IteratingActionListener<>(roleDescriptorActionListener, (rolesProvider, listener) -> { new IteratingActionListener<>(roleDescriptorActionListener, (rolesProvider, listener) -> {
// resolve descriptors with role provider // resolve descriptors with role provider
rolesProvider.accept(missing, ActionListener.wrap((resolvedDescriptors) -> { rolesProvider.accept(missing, ActionListener.wrap((resolvedDescriptors) -> {
logger.debug(() ->
new ParameterizedMessage("Roles [{}] were resolved by [{}]", names(resolvedDescriptors), rolesProvider));
builtInRoleDescriptors.addAll(resolvedDescriptors); builtInRoleDescriptors.addAll(resolvedDescriptors);
// remove resolved descriptors from the set of roles still needed to be resolved // remove resolved descriptors from the set of roles still needed to be resolved
for (RoleDescriptor descriptor : resolvedDescriptors) { for (RoleDescriptor descriptor : resolvedDescriptors) {
@ -187,6 +198,8 @@ public class CompositeRolesStore extends AbstractComponent {
return builtInRoleDescriptors; return builtInRoleDescriptors;
}).run(); }).run();
} else { } else {
logger.debug(() ->
new ParameterizedMessage("Requested roles [{}] do not exist", Strings.collectionToCommaDelimitedString(missing)));
negativeLookupCache.addAll(missing); negativeLookupCache.addAll(missing);
roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors)); roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors));
} }
@ -199,15 +212,24 @@ public class CompositeRolesStore extends AbstractComponent {
final Set<RoleDescriptor> descriptors = reservedRolesStore.roleDescriptors().stream() final Set<RoleDescriptor> descriptors = reservedRolesStore.roleDescriptors().stream()
.filter((rd) -> roleNames.contains(rd.getName())) .filter((rd) -> roleNames.contains(rd.getName()))
.collect(Collectors.toCollection(HashSet::new)); .collect(Collectors.toCollection(HashSet::new));
if (descriptors.size() > 0) {
logger.debug(() -> new ParameterizedMessage("Roles [{}] are builtin roles", names(descriptors)));
}
final Set<String> difference = difference(roleNames, descriptors); final Set<String> difference = difference(roleNames, descriptors);
if (difference.isEmpty() == false) { if (difference.isEmpty() == false) {
descriptors.addAll(fileRolesStore.roleDescriptors(difference)); final Set<RoleDescriptor> fileRoles = fileRolesStore.roleDescriptors(difference);
logger.debug(() ->
new ParameterizedMessage("Roles [{}] were resolved from [{}]", names(fileRoles), fileRolesStore.getFile()));
descriptors.addAll(fileRoles);
} }
return descriptors; return descriptors;
} }
private String names(Collection<RoleDescriptor> descriptors) {
return descriptors.stream().map(RoleDescriptor::getName).collect(Collectors.joining(","));
}
private Set<String> difference(Set<String> roleNames, Set<RoleDescriptor> descriptors) { private Set<String> difference(Set<String> roleNames, Set<RoleDescriptor> descriptors) {
Set<String> foundNames = descriptors.stream().map(RoleDescriptor::getName).collect(Collectors.toSet()); Set<String> foundNames = descriptors.stream().map(RoleDescriptor::getName).collect(Collectors.toSet());
return Sets.difference(roleNames, foundNames); return Sets.difference(roleNames, foundNames);

View File

@ -112,6 +112,10 @@ public class FileRolesStore extends AbstractComponent {
} }
} }
public Path getFile() {
return file;
}
public static Path resolveFile(Environment env) { public static Path resolveFile(Environment env) {
return XPackPlugin.resolveConfigFile(env, "roles.yml"); return XPackPlugin.resolveConfigFile(env, "roles.yml");
} }