From 3fc17ab918b662b5280a7ca1066fc78de4c883cc Mon Sep 17 00:00:00 2001 From: Albert Zaharovits Date: Thu, 11 Jan 2018 11:07:46 +0200 Subject: [PATCH] Audit Event ignore policy (elastic/x-pack-elasticsearch#3005) This commit introduces audit event filtering policies for the logging audit. Until now the only way to temper the gush of audit events was to specifically pick some event types that were reported. This superposes a way (named policies) to filter events using lucene regexp filters on the following event fields: users, realms, roles and indices. The policies are ignore policies, ie when an event matches it is ignored (not reported). Original commit: elastic/x-pack-elasticsearch@233f685121662b38a67789cf1bdc752fd6a1fae7 --- .../audit/logfile/LoggingAuditTrail.java | 471 +++-- .../logfile/LoggingAuditTrailFilterTests.java | 1579 +++++++++++++++++ .../audit/logfile/LoggingAuditTrailTests.java | 270 +-- 3 files changed, 2065 insertions(+), 255 deletions(-) create mode 100644 plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java diff --git a/plugin/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/plugin/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index 203574ccd34..27a23b05a39 100644 --- a/plugin/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/plugin/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -6,6 +6,7 @@ package org.elasticsearch.xpack.security.audit.logfile; import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.IndicesRequest; import org.elasticsearch.cluster.ClusterChangedEvent; import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.node.DiscoveryNode; @@ -26,6 +27,7 @@ import org.elasticsearch.xpack.security.audit.AuditLevel; import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.authc.AuthenticationToken; import org.elasticsearch.xpack.security.rest.RemoteHostHeader; +import org.elasticsearch.xpack.security.support.Automatons; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; import org.elasticsearch.xpack.security.user.SystemUser; import org.elasticsearch.xpack.security.user.User; @@ -37,12 +39,16 @@ import java.net.SocketAddress; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import static org.elasticsearch.common.Strings.collectionToCommaDelimitedString; import static org.elasticsearch.common.Strings.arrayToCommaDelimitedString; import static org.elasticsearch.xpack.security.SecurityField.setting; import static org.elasticsearch.xpack.security.audit.AuditLevel.ACCESS_DENIED; @@ -58,7 +64,6 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GR import static org.elasticsearch.xpack.security.audit.AuditLevel.TAMPERED_REQUEST; import static org.elasticsearch.xpack.security.audit.AuditLevel.AUTHENTICATION_SUCCESS; import static org.elasticsearch.xpack.security.audit.AuditLevel.parse; -import static org.elasticsearch.xpack.security.audit.AuditUtil.indices; import static org.elasticsearch.xpack.security.audit.AuditUtil.restRequestContent; public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, ClusterStateListener { @@ -86,10 +91,28 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, Setting.listSetting(setting("audit.logfile.events.exclude"), Collections.emptyList(), Function.identity(), Property.NodeScope); private static final Setting INCLUDE_REQUEST_BODY = Setting.boolSetting(setting("audit.logfile.events.emit_request_body"), false, Property.NodeScope); + private static final String FILTER_POLICY_PREFIX = setting("audit.logfile.events.ignore_filters."); + // because of the default wildcard value (*) for the field filter, a policy with + // an unspecified filter field will match events that have any value for that + // particular field, as well as events with that particular field missing + private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_PRINCIPALS = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "users", (key) -> Setting.listSetting(key, Collections.singletonList("*"), + Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic)); + private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_REALMS = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "realms", (key) -> Setting.listSetting(key, Collections.singletonList("*"), + Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic)); + private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_ROLES = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "roles", (key) -> Setting.listSetting(key, Collections.singletonList("*"), + Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic)); + private static final Setting.AffixSetting> FILTER_POLICY_IGNORE_INDICES = + Setting.affixKeySetting(FILTER_POLICY_PREFIX, "indices", (key) -> Setting.listSetting(key, Collections.singletonList("*"), + Function.identity(), Setting.Property.NodeScope, Setting.Property.Dynamic)); private final Logger logger; private final EnumSet events; private final boolean includeRequestBody; + // protected for testing + protected final Predicate filterPolicyPredicate; private final ThreadContext threadContext; volatile LocalNodeInfo localNodeInfo; @@ -109,12 +132,14 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, this.includeRequestBody = INCLUDE_REQUEST_BODY.get(settings); this.threadContext = threadContext; this.localNodeInfo = new LocalNodeInfo(settings, null); + this.filterPolicyPredicate = new EventFilterPolicyRegistry(settings).ignorePredicate(); clusterService.addListener(this); } @Override public void authenticationSuccess(String realm, User user, RestRequest request) { - if (events.contains(AUTHENTICATION_SUCCESS)) { + if (events.contains(AUTHENTICATION_SUCCESS) && filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), Optional.empty())) == false) { if (includeRequestBody) { logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}], request_body=[{}]", localNodeInfo.prefix, principal(user), realm, request.uri(), request.params(), restRequestContent(request)); @@ -128,32 +153,44 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { if (events.contains(AUTHENTICATION_SUCCESS)) { - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action, - message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), realm, action, + message.getClass().getSimpleName()); + } + } } } @Override public void anonymousAccessDenied(String action, TransportMessage message) { if (events.contains(ANONYMOUS_ACCESS_DENIED)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], indices=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), action, indices, - message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [anonymous_access_denied]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + } } } } @Override public void anonymousAccessDenied(RestRequest request) { - if (events.contains(ANONYMOUS_ACCESS_DENIED)) { + if (events.contains(ANONYMOUS_ACCESS_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { if (includeRequestBody) { logger.info("{}[rest] [anonymous_access_denied]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri(), restRequestContent(request)); @@ -167,30 +204,30 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) { if (events.contains(AUTHENTICATION_FAILED)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], indices=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, indices, - message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, - message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [authentication_failed]\t{}, principal=[{}], action=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, + message.getClass().getSimpleName()); + } } - } } @Override public void authenticationFailed(RestRequest request) { - if (events.contains(AUTHENTICATION_FAILED)) { + if (events.contains(AUTHENTICATION_FAILED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { if (includeRequestBody) { logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri(), restRequestContent(request)); } else { - logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), - request.uri()); + logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri()); } } } @@ -198,27 +235,31 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(String action, TransportMessage message) { if (events.contains(AUTHENTICATION_FAILED)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), action, indices, message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), action, arrayToCommaDelimitedString(indices.get()), + message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [authentication_failed]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + } } } } @Override public void authenticationFailed(AuthenticationToken token, RestRequest request) { - if (events.contains(AUTHENTICATION_FAILED)) { + if (events.contains(AUTHENTICATION_FAILED) + && filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), Optional.empty())) == false) { if (includeRequestBody) { logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), token.principal(), request.uri(), restRequestContent(request)); } else { - logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}]", localNodeInfo.prefix, - hostAttributes(request), token.principal(), request.uri()); + logger.info("{}[rest] [authentication_failed]\t{}, principal=[{}], uri=[{}]", localNodeInfo.prefix, hostAttributes(request), + token.principal(), request.uri()); } } } @@ -226,30 +267,35 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) { if (events.contains(REALM_AUTHENTICATION_FAILED)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], indices=[{}], " + - "request=[{}]", localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), - token.principal(), action, indices, message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], request=[{}]", - localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, - message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info( + "{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], indices=[{}], " + + "request=[{}]", + localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], action=[{}], request=[{}]", + localNodeInfo.prefix, realm, originAttributes(threadContext, message, localNodeInfo), token.principal(), action, + message.getClass().getSimpleName()); + } } } } @Override public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) { - if (events.contains(REALM_AUTHENTICATION_FAILED)) { + if (events.contains(REALM_AUTHENTICATION_FAILED) + && filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), Optional.empty())) == false) { if (includeRequestBody) { logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix, realm, hostAttributes(request), token.principal(), request.uri(), restRequestContent(request)); } else { - logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}]", localNodeInfo.prefix, - realm, hostAttributes(request), token.principal(), request.uri()); + logger.info("{}[rest] [realm_authentication_failed]\trealm=[{}], {}, principal=[{}], uri=[{}]", localNodeInfo.prefix, realm, + hostAttributes(request), token.principal(), request.uri()); } } } @@ -257,19 +303,21 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void accessGranted(User user, String action, TransportMessage message, String[] roleNames) { final boolean isSystem = SystemUser.is(user) || XPackUser.is(user); - final boolean logSystemAccessGranted = isSystem && events.contains(SYSTEM_ACCESS_GRANTED); - final boolean shouldLog = logSystemAccessGranted || (isSystem == false && events.contains(ACCESS_GRANTED)); - if (shouldLog) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), - arrayToCommaDelimitedString(roleNames), action, indices, message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), principal(user), arrayToCommaDelimitedString(roleNames), - action, message.getClass().getSimpleName()); + if ((isSystem && events.contains(SYSTEM_ACCESS_GRANTED)) || (isSystem == false && events.contains(ACCESS_GRANTED))) { + final Optional indices = indices(message); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), + arrayToCommaDelimitedString(roleNames), action, arrayToCommaDelimitedString(indices.get()), + message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [access_granted]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), principal(user), + arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); + } } } } @@ -277,26 +325,30 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void accessDenied(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(ACCESS_DENIED)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), - arrayToCommaDelimitedString(roleNames), action, indices, message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), principal(user), arrayToCommaDelimitedString(roleNames), - action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), principal(user), + arrayToCommaDelimitedString(roleNames), action, arrayToCommaDelimitedString(indices.get()), + message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [access_denied]\t{}, {}, roles=[{}], action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), principal(user), + arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); + } } } } @Override public void tamperedRequest(RestRequest request) { - if (events.contains(TAMPERED_REQUEST)) { + if (events.contains(TAMPERED_REQUEST) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { if (includeRequestBody) { - logger.info("{}[rest] [tampered_request]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, - hostAttributes(request), request.uri(), restRequestContent(request)); + logger.info("{}[rest] [tampered_request]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), + request.uri(), restRequestContent(request)); } else { logger.info("{}[rest] [tampered_request]\t{}, uri=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri()); } @@ -306,14 +358,17 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void tamperedRequest(String action, TransportMessage message) { if (events.contains(TAMPERED_REQUEST)) { - String indices = indicesString(message); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [tampered_request]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), action, indices, message.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [tampered_request]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [tampered_request]\t{}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), action, arrayToCommaDelimitedString(indices.get()), + message.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [tampered_request]\t{}, action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, message, localNodeInfo), action, message.getClass().getSimpleName()); + } } } } @@ -321,59 +376,95 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void tamperedRequest(User user, String action, TransportMessage request) { if (events.contains(TAMPERED_REQUEST)) { - String indices = indicesString(request); - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - if (indices != null) { - logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, request, localNodeInfo), principal(user), action, indices, - request.getClass().getSimpleName()); - } else { - logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], request=[{}]", localNodeInfo.prefix, - originAttributes(threadContext, request, localNodeInfo), principal(user), action, - request.getClass().getSimpleName()); + final Optional indices = indices(request); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.empty(), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], indices=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, request, localNodeInfo), principal(user), action, + arrayToCommaDelimitedString(indices.get()), request.getClass().getSimpleName()); + } else { + logger.info("{}[transport] [tampered_request]\t{}, {}, action=[{}], request=[{}]", localNodeInfo.prefix, + originAttributes(threadContext, request, localNodeInfo), principal(user), action, + request.getClass().getSimpleName()); + } } } } @Override public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { - if (events.contains(CONNECTION_GRANTED)) { - logger.info("{}[ip_filter] [connection_granted]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", - localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); + if (events.contains(CONNECTION_GRANTED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + logger.info("{}[ip_filter] [connection_granted]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix, + NetworkAddress.format(inetAddress), profile, rule); } } @Override public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { - if (events.contains(CONNECTION_DENIED)) { - logger.info("{}[ip_filter] [connection_denied]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", - localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); + if (events.contains(CONNECTION_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + logger.info("{}[ip_filter] [connection_denied]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix, + NetworkAddress.format(inetAddress), profile, rule); } } @Override public void runAsGranted(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(RUN_AS_GRANTED)) { - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - logger.info("{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}], action=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), user.authenticatedUser().principal(), - user.principal(), arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info( + "{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}]," + + " action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), + user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info( + "{}[transport] [run_as_granted]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}]," + + " action=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), + user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action, + message.getClass().getSimpleName()); + } + } } } @Override public void runAsDenied(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(RUN_AS_DENIED)) { - final LocalNodeInfo localNodeInfo = this.localNodeInfo; - logger.info("{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}], action=[{}], request=[{}]", - localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), user.authenticatedUser().principal(), - user.principal(), arrayToCommaDelimitedString(roleNames), action, message.getClass().getSimpleName()); + final Optional indices = indices(message); + if (filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { + final LocalNodeInfo localNodeInfo = this.localNodeInfo; + if (indices.isPresent()) { + logger.info( + "{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}]," + + " action=[{}], indices=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), + user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action, + arrayToCommaDelimitedString(indices.get()), message.getClass().getSimpleName()); + } else { + logger.info( + "{}[transport] [run_as_denied]\t{}, principal=[{}], run_as_principal=[{}], roles=[{}]," + + " action=[{}], request=[{}]", + localNodeInfo.prefix, originAttributes(threadContext, message, localNodeInfo), + user.authenticatedUser().principal(), user.principal(), arrayToCommaDelimitedString(roleNames), action, + message.getClass().getSimpleName()); + } + } } } @Override public void runAsDenied(User user, RestRequest request, String[] roleNames) { - if (events.contains(RUN_AS_DENIED)) { + if (events.contains(RUN_AS_DENIED) && filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), Optional.empty())) == false) { if (includeRequestBody) { logger.info("{}[rest] [run_as_denied]\t{}, principal=[{}], roles=[{}], uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), user.principal(), arrayToCommaDelimitedString(roleNames), request.uri(), @@ -387,7 +478,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, private static String hostAttributes(RestRequest request) { String formattedAddress; - SocketAddress socketAddress = request.getRemoteAddress(); + final SocketAddress socketAddress = request.getRemoteAddress(); if (socketAddress instanceof InetSocketAddress) { formattedAddress = NetworkAddress.format(((InetSocketAddress) socketAddress).getAddress()); } else { @@ -401,7 +492,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, } private static Optional restOriginTag(ThreadContext threadContext) { - InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext); + final InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext); if (restAddress == null) { return Optional.empty(); } @@ -411,7 +502,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, } private static Optional transportOriginTag(TransportMessage message) { - TransportAddress address = message.remoteAddress(); + final TransportAddress address = message.remoteAddress(); if (address == null) { return Optional.empty(); } @@ -421,13 +512,15 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, .toString()); } - static String indicesString(TransportMessage message) { - Set indices = indices(message); - return indices == null ? null : collectionToCommaDelimitedString(indices); + static Optional indices(TransportMessage message) { + if (message instanceof IndicesRequest) { + return Optional.of(((IndicesRequest) message).indices()); + } + return Optional.empty(); } static String principal(User user) { - StringBuilder builder = new StringBuilder("principal=["); + final StringBuilder builder = new StringBuilder("principal=["); builder.append(user.principal()); if (user.isRunAs()) { builder.append("], run_by_principal=[").append(user.authenticatedUser().principal()); @@ -442,6 +535,132 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, settings.add(INCLUDE_EVENT_SETTINGS); settings.add(EXCLUDE_EVENT_SETTINGS); settings.add(INCLUDE_REQUEST_BODY); + settings.add(FILTER_POLICY_IGNORE_PRINCIPALS); + settings.add(FILTER_POLICY_IGNORE_INDICES); + settings.add(FILTER_POLICY_IGNORE_ROLES); + settings.add(FILTER_POLICY_IGNORE_REALMS); + } + + /** + * Builds the predicate for a single policy filter. The predicate matches events + * that will be ignored, aka filtered out, aka not logged. The event can be + * filtered by the following fields : `user`, `realm`, `role` and `index`. + * Predicates on each field are ANDed together to form the filter predicate of + * the policy. + */ + private static final class EventFilterPolicy { + final String name; + private final Predicate ignorePrincipalsPredicate; + private final Predicate ignoreRealmsPredicate; + private final Predicate ignoreRolesPredicate; + private final Predicate ignoreIndicesPredicate; + + /** + * An empty filter list for a field will match events with that field missing. + * An event with an undefined field has the field value the empty string ("") or + * a singleton list of the empty string ([""]). + */ + EventFilterPolicy(String name, Settings settings) { + this.name = name; + ignorePrincipalsPredicate = Automatons.predicate( + emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_PRINCIPALS.getConcreteSettingForNamespace(name).get(settings))); + ignoreRealmsPredicate = Automatons.predicate( + emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_REALMS.getConcreteSettingForNamespace(name).get(settings))); + ignoreRolesPredicate = Automatons.predicate( + emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_ROLES.getConcreteSettingForNamespace(name).get(settings))); + ignoreIndicesPredicate = Automatons.predicate( + emptyStringBuildsEmptyAutomaton(FILTER_POLICY_IGNORE_INDICES.getConcreteSettingForNamespace(name).get(settings))); + } + + /** + * It is a requirement that empty string filters match empty string fields. In + * this case we require automatons from empty string to match the empty string. + * `Automatons.predicate("").test("") == false` + * `Automatons.predicate("//").test("") == true` + */ + private List emptyStringBuildsEmptyAutomaton(List l) { + if (l.isEmpty()) { + return Collections.singletonList("//"); + } + return l.stream().map(f -> f.isEmpty() ? "//" : f).collect(Collectors.toList()); + } + + /** + * ANDs the predicates of this filter policy. The `indices` and `roles` fields + * of an audit event are multi-valued and all values should match the filter + * predicate of the corresponding field. + */ + Predicate ignorePredicate() { + return (eventInfo) -> { + return ignorePrincipalsPredicate.test(eventInfo.principal) + && ignoreRealmsPredicate.test(eventInfo.realm) + && eventInfo.roles.get().allMatch(ignoreRolesPredicate) + && eventInfo.indices.get().allMatch(ignoreIndicesPredicate); + }; + } + } + + /** + * Builds the filter predicates for all the policies. Predicates of all policies + * are ORed together, so that an audit event matching any policy is ignored. + */ + private static final class EventFilterPolicyRegistry { + private final Map> policyMap; + + EventFilterPolicyRegistry(Settings settings) { + final Map> map = new HashMap<>(); + for (final String policyName : settings.getGroups(FILTER_POLICY_PREFIX, true).keySet()) { + map.put(policyName, new EventFilterPolicy(policyName, settings).ignorePredicate()); + } + policyMap = Collections.unmodifiableMap(map); + } + + Predicate ignorePredicate() { + return policyMap.values().stream().reduce(x -> false, (x, y) -> x.or(y)); + } + } + + /** + * Abstraction for the fields of the audit event that are used for filtering. If + * an event has a missing field (one of `user`, `realm`, `roles` and `indices`) + * the value for the field will be the empty string or a singleton stream of the + * empty string. + */ + static final class AuditEventMetaInfo { + final String principal; + final String realm; + final Supplier> roles; + final Supplier> indices; + + // empty is used for events can be filtered out only by the lack of a field + static final AuditEventMetaInfo EMPTY = new AuditEventMetaInfo(Optional.empty(), Optional.empty(), Optional.empty()); + + /** + * If a field is missing for an event, its value for filtering purposes is the + * empty string or a singleton stream of the empty string. This a allows a + * policy to filter by the missing value using the empty string, ie + * `ignore_filters.users: ["", "elastic"]` will filter events with a missing + * user field (such as `anonymous_access_denied`) as well as events from the + * "elastic" username. + */ + AuditEventMetaInfo(Optional user, Optional realm, Optional roles, Optional indices) { + this.principal = user.map(u -> u.principal()).orElse(""); + this.realm = realm.orElse(""); + // Supplier indirection and lazy generation of Streams serves 2 purposes: + // 1. streams might not get generated due to short circuiting logical + // conditions on the `principal` and `realm` fields + // 2. reusability of the AuditEventMetaInfo instance: in this case Streams have + // to be regenerated as they cannot be operated upon twice + this.roles = () -> roles.filter(r -> r.length != 0).map(Arrays::stream).orElse(Stream.of("")); + this.indices = () -> indices.filter(i -> i.length != 0).map(Arrays::stream).orElse(Stream.of("")); + } + + AuditEventMetaInfo(Optional authenticationToken, Optional realm, Optional indices) { + this.principal = authenticationToken.map(u -> u.principal()).orElse(""); + this.realm = realm.orElse(""); + this.roles = () -> Stream.of(""); + this.indices = () -> indices.filter(r -> r.length != 0).map(i -> Arrays.stream(i)).orElse(Stream.of("")); + } } @Override @@ -458,7 +677,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, } } - protected static class LocalNodeInfo { + static class LocalNodeInfo { private final DiscoveryNode localNode; private final String prefix; private final String localOriginTag; @@ -472,19 +691,19 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, static String resolvePrefix(Settings settings, @Nullable DiscoveryNode localNode) { final StringBuilder builder = new StringBuilder(); if (HOST_ADDRESS_SETTING.get(settings)) { - String address = localNode != null ? localNode.getHostAddress() : null; + final String address = localNode != null ? localNode.getHostAddress() : null; if (address != null) { builder.append("[").append(address).append("] "); } } if (HOST_NAME_SETTING.get(settings)) { - String hostName = localNode != null ? localNode.getHostName() : null; + final String hostName = localNode != null ? localNode.getHostName() : null; if (hostName != null) { builder.append("[").append(hostName).append("] "); } } if (NODE_NAME_SETTING.get(settings)) { - String name = settings.get("name"); + final String name = settings.get("name"); if (name != null) { builder.append("[").append(name).append("] "); } diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java new file mode 100644 index 00000000000..8f361a249be --- /dev/null +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java @@ -0,0 +1,1579 @@ +/* + * 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.audit.logfile; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.cluster.node.DiscoveryNode; +import org.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.mock.orig.Mockito; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.elasticsearch.test.rest.FakeRestRequest.Builder; +import org.elasticsearch.transport.TransportMessage; +import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrail.AuditEventMetaInfo; +import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.MockMessage; +import org.elasticsearch.xpack.security.audit.logfile.LoggingAuditTrailTests.RestContent; +import org.elasticsearch.xpack.security.authc.AuthenticationToken; +import org.elasticsearch.xpack.security.rest.RemoteHostHeader; +import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; +import org.elasticsearch.xpack.security.user.SystemUser; +import org.elasticsearch.xpack.security.user.User; +import org.junit.Before; +import org.mockito.stubbing.Answer; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LoggingAuditTrailFilterTests extends ESTestCase { + + private static final String FILTER_MARKER = "filterMarker_"; + private static final String UNFILTER_MARKER = "nofilter_"; + + private Settings settings; + private DiscoveryNode localNode; + private ClusterService clusterService; + private ThreadContext threadContext; + private Logger logger; + List logOutput; + + @Before + public void init() throws Exception { + settings = Settings.builder() + .put("xpack.security.audit.logfile.prefix.emit_node_host_address", randomBoolean()) + .put("xpack.security.audit.logfile.prefix.emit_node_host_name", randomBoolean()) + .put("xpack.security.audit.logfile.prefix.emit_node_name", randomBoolean()) + .put("xpack.security.audit.logfile.events.emit_request_body", randomBoolean()) + .put("xpack.security.audit.logfile.events.include", "_all") + .build(); + localNode = mock(DiscoveryNode.class); + when(localNode.getHostAddress()).thenReturn(buildNewFakeTransportAddress().toString()); + clusterService = mock(ClusterService.class); + when(clusterService.localNode()).thenReturn(localNode); + Mockito.doAnswer((Answer) invocation -> { + final LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0]; + arg0.updateLocalNodeInfo(localNode); + return null; + }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); + threadContext = new ThreadContext(Settings.EMPTY); + logger = CapturingLogger.newCapturingLogger(Level.INFO); + logOutput = CapturingLogger.output(logger.getName(), Level.INFO); + } + + public void testSingleCompletePolicyPredicate() throws Exception { + // create complete filter policy + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + // filter by username + final List filteredUsernames = randomNonEmptyListOfFilteredNames(); + final List filteredUsers = filteredUsernames.stream().map(u -> { + if (randomBoolean()) { + return new User(u); + } else { + return new User(new User(u), new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4))); + } + }).collect(Collectors.toList()); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.users", filteredUsernames); + // filter by realms + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.realms", filteredRealms); + // filter by roles + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.roles", filteredRoles); + // filter by indices + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.indices", filteredIndices); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + + // all fields match + assertTrue("Matches the filter predicate.", auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo( + Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final User unfilteredUser; + if (randomBoolean()) { + unfilteredUser = new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } else { + unfilteredUser = new User(new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + new User(randomFrom(filteredUsers).principal())); + } + // one field does not match or is empty + assertFalse("Does not match the filter predicate because of the user.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(unfilteredUser), + Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertFalse("Does not match the filter predicate because of the empty user.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertFalse("Does not match the filter predicate because of the realm.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertFalse("Does not match the filter predicate because of the empty realm.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.empty(), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final List someRolesDoNotMatch = new ArrayList<>(randomSubsetOf(randomIntBetween(0, filteredRoles.size()), filteredRoles)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someRolesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + assertFalse("Does not match the filter predicate because of some of the roles.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), Optional.of(someRolesDoNotMatch.toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final Optional emptyRoles = randomBoolean() ? Optional.empty() : Optional.of(new String[0]); + assertFalse("Does not match the filter predicate because of the empty roles.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), emptyRoles, + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final List someIndicesDoNotMatch = new ArrayList<>( + randomSubsetOf(randomIntBetween(0, filteredIndices.size()), filteredIndices)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someIndicesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + assertFalse("Does not match the filter predicate because of some of the indices.", auditTrail.filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(someIndicesDoNotMatch.toArray(new String[0]))))); + final Optional emptyIndices = randomBoolean() ? Optional.empty() : Optional.of(new String[0]); + assertFalse("Does not match the filter predicate because of the empty indices.", auditTrail.filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + emptyIndices))); + } + + public void testSingleCompleteWithEmptyFieldPolicyPredicate() throws Exception { + // create complete filter policy + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + // filter by username + final List filteredUsernames = randomNonEmptyListOfFilteredNames(); + final List filteredUsers = filteredUsernames.stream().map(u -> { + if (randomBoolean()) { + return new User(u); + } else { + return new User(new User(u), new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4))); + } + }).collect(Collectors.toList()); + filteredUsernames.add(""); // filter by missing user name + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.users", filteredUsernames); + // filter by realms + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + filteredRealms.add(""); // filter by missing realm name + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.realms", filteredRealms); + filteredRealms.remove(""); + // filter by roles + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + filteredRoles.add(""); // filter by missing role name + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.roles", filteredRoles); + filteredRoles.remove(""); + // filter by indices + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + filteredIndices.add(""); // filter by missing index name + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.completeFilterPolicy.indices", filteredIndices); + filteredIndices.remove(""); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + + // all fields match + assertTrue("Matches the filter predicate.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final User unfilteredUser; + if (randomBoolean()) { + unfilteredUser = new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } else { + unfilteredUser = new User(new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + new User(randomFrom(filteredUsers).principal())); + } + // one field does not match or is empty + assertFalse("Does not match the filter predicate because of the user.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(unfilteredUser), + Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertTrue("Matches the filter predicate because of the empty user.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertFalse("Does not match the filter predicate because of the realm.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + assertTrue("Matches the filter predicate because of the empty realm.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.empty(), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final List someRolesDoNotMatch = new ArrayList<>(randomSubsetOf(randomIntBetween(0, filteredRoles.size()), filteredRoles)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someRolesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + assertFalse("Does not match the filter predicate because of some of the roles.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), Optional.of(someRolesDoNotMatch.toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final Optional emptyRoles = randomBoolean() ? Optional.empty() : Optional.of(new String[0]); + assertTrue("Matches the filter predicate because of the empty roles.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), emptyRoles, + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + final List someIndicesDoNotMatch = new ArrayList<>( + randomSubsetOf(randomIntBetween(0, filteredIndices.size()), filteredIndices)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someIndicesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + assertFalse("Does not match the filter predicate because of some of the indices.", auditTrail.filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(someIndicesDoNotMatch.toArray(new String[0]))))); + final Optional emptyIndices = randomBoolean() ? Optional.empty() : Optional.of(new String[0]); + assertTrue("Matches the filter predicate because of the empty indices.", auditTrail.filterPolicyPredicate + .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + emptyIndices))); + } + + public void testTwoPolicyPredicatesWithMissingFields() throws Exception { + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + // first policy: realms and roles filters + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.firstPolicy.realms", filteredRealms); + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.firstPolicy.roles", filteredRoles); + // second policy: users and indices filters + final List filteredUsernames = randomNonEmptyListOfFilteredNames(); + final List filteredUsers = filteredUsernames.stream().map(u -> { + if (randomBoolean()) { + return new User(u); + } else { + return new User(new User(u), new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4))); + } + }).collect(Collectors.toList()); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.secondPolicy.users", filteredUsernames); + // filter by indices + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.secondPolicy.indices", filteredIndices); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + + final User unfilteredUser; + if (randomBoolean()) { + unfilteredUser = new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } else { + unfilteredUser = new User(new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + new User(randomFrom(filteredUsers).principal())); + } + final List someRolesDoNotMatch = new ArrayList<>(randomSubsetOf(randomIntBetween(0, filteredRoles.size()), filteredRoles)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someRolesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + final List someIndicesDoNotMatch = new ArrayList<>( + randomSubsetOf(randomIntBetween(0, filteredIndices.size()), filteredIndices)); + for (int i = 0; i < randomIntBetween(1, 8); i++) { + someIndicesDoNotMatch.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)); + } + // matches both the first and the second policies + assertTrue("Matches both the first and the second filter predicates.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + // matches first policy but not the second + assertTrue("Matches the first filter predicate but not the second.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(unfilteredUser), + Optional.of(randomFrom(filteredRealms)), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), + Optional.of(someIndicesDoNotMatch.toArray(new String[0]))))); + // matches the second policy but not the first + assertTrue("Matches the second filter predicate but not the first.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + Optional.of(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + Optional.of(someRolesDoNotMatch.toArray(new String[0])), + Optional.of(randomSubsetOf(randomIntBetween(1, filteredIndices.size()), filteredIndices).toArray(new String[0]))))); + // matches neither the first nor the second policies + assertFalse("Matches neither the first nor the second filter predicates.", + auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(unfilteredUser), + Optional.of(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 8)), + Optional.of(someRolesDoNotMatch.toArray(new String[0])), + Optional.of(someIndicesDoNotMatch.toArray(new String[0]))))); + } + + public void testUsersFilter() throws Exception { + final List allFilteredUsers = new ArrayList<>(); + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + final List filteredUsers = randomNonEmptyListOfFilteredNames(); + allFilteredUsers.addAll(filteredUsers); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.policy" + i + ".users", filteredUsers); + } + // a filter for a field consisting of an empty string ("") or an empty list([]) + // will match events that lack that field + final boolean filterMissingUser = randomBoolean(); + if (filterMissingUser) { + if (randomBoolean()) { + final List filteredUsers = randomNonEmptyListOfFilteredNames(); + // possibly renders list empty + filteredUsers.remove(0); + allFilteredUsers.addAll(filteredUsers); + filteredUsers.add(""); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.users", filteredUsers); + } else { + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.users", + Collections.emptyList()); + } + } + User filteredUser; + if (randomBoolean()) { + filteredUser = new User(randomFrom(allFilteredUsers), new String[] { "r1" }, new User("authUsername", new String[] { "r2" })); + } else { + filteredUser = new User(randomFrom(allFilteredUsers), new String[] { "r1" }); + } + User unfilteredUser; + if (randomBoolean()) { + unfilteredUser = new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4), new String[] { "r1" }, + new User("authUsername", new String[] { "r2" })); + } else { + unfilteredUser = new User(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4), new String[] { "r1" }); + } + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) + : new MockIndicesRequest(threadContext, new String[] { "idx1", "idx2" }); + final MockToken filteredToken = new MockToken(randomFrom(allFilteredUsers)); + final MockToken unfilteredToken = new MockToken(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + // anonymous accessDenied + auditTrail.anonymousAccessDenied("_action", message); + if (filterMissingUser) { + assertThat("Anonymous message: not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous message: filtered out by the user filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied(getRestRequest()); + if (filterMissingUser) { + assertThat("Anonymous rest request: not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous rest request: filtered out by user filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authenticationFailed + auditTrail.authenticationFailed(getRestRequest()); + if (filterMissingUser) { + assertThat("AuthenticationFailed no token rest request: not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(unfilteredToken, "_action", message); + assertThat("AuthenticationFailed token request: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(filteredToken, "_action", message); + assertThat("AuthenticationFailed token request: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", message); + if (filterMissingUser) { + assertThat("AuthenticationFailed no token message: not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(unfilteredToken, getRestRequest()); + assertThat("AuthenticationFailed rest request: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(filteredToken, getRestRequest()); + assertThat("AuthenticationFailed rest request: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", unfilteredToken, "_action", message); + assertThat("AuthenticationFailed realm message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", filteredToken, "_action", message); + assertThat("AuthenticationFailed realm message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", unfilteredToken, getRestRequest()); + assertThat("AuthenticationFailed realm rest request: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", filteredToken, getRestRequest()); + assertThat("AuthenticationFailed realm rest request: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // accessGranted + auditTrail.accessGranted(unfilteredUser, "_action", message, new String[] { "role1" }); + assertThat("AccessGranted message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(filteredUser, "_action", message, new String[] { "role1" }); + assertThat("AccessGranted message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessGranted internal message: system user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(unfilteredUser, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessGranted internal message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(filteredUser, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessGranted internal message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // accessDenied + auditTrail.accessDenied(unfilteredUser, "_action", message, new String[] { "role1" }); + assertThat("AccessDenied message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(filteredUser, "_action", message, new String[] { "role1" }); + assertThat("AccessDenied message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessDenied internal message: system user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(unfilteredUser, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessDenied internal message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(filteredUser, "internal:_action", message, new String[] { "role1" }); + assertThat("AccessDenied internal message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // tamperedRequest + auditTrail.tamperedRequest(getRestRequest()); + if (filterMissingUser) { + assertThat("Tampered rest: is not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Tampered rest: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.tamperedRequest("_action", message); + if (filterMissingUser) { + assertThat("Tampered message: is not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Tampered message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.tamperedRequest(unfilteredUser, "_action", message); + assertThat("Tampered message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.tamperedRequest(filteredUser, "_action", message); + assertThat("Tampered message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // connection denied + auditTrail.connectionDenied(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingUser) { + assertThat("Connection denied: is not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Connection denied: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // connection granted + auditTrail.connectionGranted(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingUser) { + assertThat("Connection granted: is not filtered out by the missing user filter", logOutput.size(), is(0)); + } else { + assertThat("Connection granted: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // runAsGranted + auditTrail.runAsGranted(unfilteredUser, "_action", new MockMessage(threadContext), new String[] { "role1" }); + assertThat("RunAsGranted message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsGranted(filteredUser, "_action", new MockMessage(threadContext), new String[] { "role1" }); + assertThat("RunAsGranted message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // runAsDenied + auditTrail.runAsDenied(unfilteredUser, "_action", new MockMessage(threadContext), new String[] { "role1" }); + assertThat("RunAsDenied message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(filteredUser, "_action", new MockMessage(threadContext), new String[] { "role1" }); + assertThat("RunAsDenied message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(unfilteredUser, getRestRequest(), new String[] { "role1" }); + assertThat("RunAsDenied rest request: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(filteredUser, getRestRequest(), new String[] { "role1" }); + assertThat("RunAsDenied rest request: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // authentication Success + auditTrail.authenticationSuccess("_realm", unfilteredUser, getRestRequest()); + assertThat("AuthenticationSuccess rest request: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", filteredUser, getRestRequest()); + assertThat("AuthenticationSuccess rest request: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", unfilteredUser, "_action", message); + assertThat("AuthenticationSuccess message: unfiltered user is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", filteredUser, "_action", message); + assertThat("AuthenticationSuccess message: filtered user is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + } + + public void testRealmsFilter() throws Exception { + final List allFilteredRealms = new ArrayList<>(); + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + allFilteredRealms.addAll(filteredRealms); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.policy" + i + ".realms", filteredRealms); + } + // a filter for a field consisting of an empty string ("") or an empty list([]) + // will match events that lack that field + final boolean filterMissingRealm = randomBoolean(); + if (filterMissingRealm) { + if (randomBoolean()) { + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + // possibly renders list empty + filteredRealms.remove(0); + allFilteredRealms.addAll(filteredRealms); + filteredRealms.add(""); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.realms", filteredRealms); + } else { + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.realms", + Collections.emptyList()); + } + } + final String filteredRealm = randomFrom(allFilteredRealms); + final String unfilteredRealm = UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4); + User user; + if (randomBoolean()) { + user = new User("user1", new String[] { "r1" }, new User("authUsername", new String[] { "r2" })); + } else { + user = new User("user1", new String[] { "r1" }); + } + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) + : new MockIndicesRequest(threadContext, new String[] { "idx1", "idx2" }); + final MockToken authToken = new MockToken("token1"); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + // anonymous accessDenied + auditTrail.anonymousAccessDenied("_action", message); + if (filterMissingRealm) { + assertThat("Anonymous message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous message: filtered out by the realm filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied(getRestRequest()); + if (filterMissingRealm) { + assertThat("Anonymous rest request: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous rest request: filtered out by realm filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authenticationFailed + auditTrail.authenticationFailed(getRestRequest()); + if (filterMissingRealm) { + assertThat("AuthenticationFailed no token rest request: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, "_action", message); + if (filterMissingRealm) { + assertThat("AuthenticationFailed token request: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed token request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", message); + if (filterMissingRealm) { + assertThat("AuthenticationFailed no token message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, getRestRequest()); + if (filterMissingRealm) { + assertThat("AuthenticationFailed rest request: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(unfilteredRealm, authToken, "_action", message); + assertThat("AuthenticationFailed realm message: unfiltered realm is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(filteredRealm, authToken, "_action", message); + assertThat("AuthenticationFailed realm message: filtered realm is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(unfilteredRealm, authToken, getRestRequest()); + assertThat("AuthenticationFailed realm rest request: unfiltered realm is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(filteredRealm, authToken, getRestRequest()); + assertThat("AuthenticationFailed realm rest request: filtered realm is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // accessGranted + auditTrail.accessGranted(user, "_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessGranted message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessGranted message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessGranted internal message system user: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessGranted internal message system user: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "internal:_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessGranted internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessGranted internal message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // accessDenied + auditTrail.accessDenied(user, "_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessDenied message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessDenied message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessDenied internal message system user: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessDenied internal message system user: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "internal:_action", message, new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("AccessGranted internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("AccessGranted internal message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // tamperedRequest + auditTrail.tamperedRequest(getRestRequest()); + if (filterMissingRealm) { + assertThat("Tampered rest: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Tampered rest: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.tamperedRequest("_action", message); + if (filterMissingRealm) { + assertThat("Tampered message: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Tampered message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.tamperedRequest(user, "_action", message); + if (filterMissingRealm) { + assertThat("Tampered message: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Tampered message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // connection denied + auditTrail.connectionDenied(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingRealm) { + assertThat("Connection denied: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Connection denied: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // connection granted + auditTrail.connectionGranted(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingRealm) { + assertThat("Connection granted: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("Connection granted: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // runAsGranted + auditTrail.runAsGranted(user, "_action", new MockMessage(threadContext), new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("RunAsGranted message: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsGranted message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // runAsDenied + auditTrail.runAsDenied(user, "_action", new MockMessage(threadContext), new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("RunAsDenied message: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsDenied message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, getRestRequest(), new String[] { "role1" }); + if (filterMissingRealm) { + assertThat("RunAsDenied rest request: is not filtered out by the missing realm filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsDenied rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authentication Success + auditTrail.authenticationSuccess(unfilteredRealm, user, getRestRequest()); + assertThat("AuthenticationSuccess rest request: unfiltered realm is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess(filteredRealm, user, getRestRequest()); + assertThat("AuthenticationSuccess rest request: filtered realm is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess(unfilteredRealm, user, "_action", message); + assertThat("AuthenticationSuccess message: unfiltered realm is filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess(filteredRealm, user, "_action", message); + assertThat("AuthenticationSuccess message: filtered realm is not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + } + + public void testRolesFilter() throws Exception { + final List> allFilteredRoles = new ArrayList<>(); + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + allFilteredRoles.add(new ArrayList<>(filteredRoles)); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.policy" + i + ".roles", filteredRoles); + } + // a filter for a field consisting of an empty string ("") or an empty list([]) + // will match events that lack that field + final boolean filterMissingRoles = randomBoolean(); + if (filterMissingRoles) { + if (randomBoolean()) { + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + // possibly renders list empty + filteredRoles.remove(0); + if (filteredRoles.isEmpty() == false) { + allFilteredRoles.add(new ArrayList<>(filteredRoles)); + } + filteredRoles.add(""); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.roles", filteredRoles); + } else { + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.roles", + Collections.emptyList()); + } + } + // filtered roles are a subset of the roles of any policy + final List filterPolicy = randomFrom(allFilteredRoles); + final String[] filteredRoles = randomListFromLengthBetween(filterPolicy, 1, filterPolicy.size()).toArray(new String[0]); + // unfiltered role sets either have roles distinct from any other policy or are + // a mix of roles from 2 or more policies + final List unfilteredPolicy = randomFrom(allFilteredRoles); + List _unfilteredRoles; + if (randomBoolean()) { + _unfilteredRoles = randomListFromLengthBetween(unfilteredPolicy, 0, unfilteredPolicy.size()); + // add roles distinct from any role in any filter policy + for (int i = 0; i < randomIntBetween(1, 4); i++) { + _unfilteredRoles.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + } + } else { + _unfilteredRoles = randomListFromLengthBetween(unfilteredPolicy, 1, unfilteredPolicy.size()); + // add roles from other filter policies + final List otherRoles = new ArrayList<>(); + for (int j = 0; j < randomIntBetween(1, 4); j++) { + otherRoles.add(FILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + } + _unfilteredRoles.addAll(randomListFromLengthBetween(otherRoles, 1, otherRoles.size())); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.otherPolicy.roles", otherRoles); + } + final String[] unfilteredRoles = _unfilteredRoles.toArray(new String[0]); + User user; + if (randomBoolean()) { + user = new User("user1", new String[] { "r1" }, new User("authUsername", new String[] { "r2" })); + } else { + user = new User("user1", new String[] { "r1" }); + } + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) + : new MockIndicesRequest(threadContext, new String[] { "idx1", "idx2" }); + final MockToken authToken = new MockToken("token1"); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + // anonymous accessDenied + auditTrail.anonymousAccessDenied("_action", message); + if (filterMissingRoles) { + assertThat("Anonymous message: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous message: filtered out by the roles filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied(getRestRequest()); + if (filterMissingRoles) { + assertThat("Anonymous rest request: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous rest request: filtered out by roles filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authenticationFailed + auditTrail.authenticationFailed(getRestRequest()); + if (filterMissingRoles) { + assertThat("AuthenticationFailed no token rest request: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, "_action", message); + if (filterMissingRoles) { + assertThat("AuthenticationFailed token request: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed token request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", message); + if (filterMissingRoles) { + assertThat("AuthenticationFailed no token message: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed no token message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, getRestRequest()); + if (filterMissingRoles) { + assertThat("AuthenticationFailed rest request: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, "_action", message); + if (filterMissingRoles) { + assertThat("AuthenticationFailed realm message: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed realm message: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, getRestRequest()); + if (filterMissingRoles) { + assertThat("AuthenticationFailed realm rest request: not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed realm rest request: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // accessGranted + auditTrail.accessGranted(user, "_action", message, unfilteredRoles); + assertThat("AccessGranted message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "_action", message, filteredRoles); + assertThat("AccessGranted message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, unfilteredRoles); + assertThat("AccessGranted internal message system user: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, filteredRoles); + assertThat("AccessGranted internal message system user: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "internal:_action", message, unfilteredRoles); + assertThat("AccessGranted internal message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "internal:_action", message, filteredRoles); + assertThat("AccessGranted internal message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // accessDenied + auditTrail.accessDenied(user, "_action", message, unfilteredRoles); + assertThat("AccessDenied message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "_action", message, filteredRoles); + assertThat("AccessDenied message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", message, unfilteredRoles); + assertThat("AccessDenied internal message system user: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", message, filteredRoles); + assertThat("AccessDenied internal message system user: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "internal:_action", message, unfilteredRoles); + assertThat("AccessDenied internal message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "internal:_action", message, filteredRoles); + assertThat("AccessDenied internal message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // connection denied + auditTrail.connectionDenied(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingRoles) { + assertThat("Connection denied: is not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("Connection denied: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // connection granted + auditTrail.connectionGranted(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingRoles) { + assertThat("Connection granted: is not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("Connection granted: is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // runAsGranted + auditTrail.runAsGranted(user, "_action", new MockMessage(threadContext), unfilteredRoles); + assertThat("RunAsGranted message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsGranted(user, "_action", new MockMessage(threadContext), filteredRoles); + assertThat("RunAsGranted message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // runAsDenied + auditTrail.runAsDenied(user, "_action", new MockMessage(threadContext), unfilteredRoles); + assertThat("RunAsDenied message: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, "_action", new MockMessage(threadContext), filteredRoles); + assertThat("RunAsDenied message: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, getRestRequest(), unfilteredRoles); + assertThat("RunAsDenied rest request: unfiltered roles filtered out", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, getRestRequest(), filteredRoles); + assertThat("RunAsDenied rest request: filtered roles not filtered out", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // authentication Success + auditTrail.authenticationSuccess("_realm", user, getRestRequest()); + if (filterMissingRoles) { + assertThat("AuthenticationSuccess rest request: is not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationSuccess rest request: unfiltered realm is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", user, "_action", message); + if (filterMissingRoles) { + assertThat("AuthenticationSuccess message: is not filtered out by the missing roles filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationSuccess message: unfiltered realm is filtered out", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + } + + public void testIndicesFilter() throws Exception { + final List> allFilteredIndices = new ArrayList<>(); + final Settings.Builder settingsBuilder = Settings.builder().put(settings); + for (int i = 0; i < randomIntBetween(1, 3); i++) { + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + allFilteredIndices.add(new ArrayList<>(filteredIndices)); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.policy" + i + ".indices", filteredIndices); + } + // a filter for a field consisting of an empty string ("") or an empty list([]) + // will match events that lack that field + final boolean filterMissingIndices = randomBoolean(); + if (filterMissingIndices) { + if (randomBoolean()) { + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + // possibly renders list empty + filteredIndices.remove(0); + if (filteredIndices.isEmpty() == false) { + allFilteredIndices.add(new ArrayList<>(filteredIndices)); + } + filteredIndices.add(""); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.indices", filteredIndices); + } else { + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.missingPolicy.indices", + Collections.emptyList()); + } + } + // filtered roles are a subset of the roles of any policy + final List filterPolicy = randomFrom(allFilteredIndices); + final String[] filteredIndices = randomListFromLengthBetween(filterPolicy, 1, filterPolicy.size()).toArray(new String[0]); + // unfiltered index sets either have indices distinct from any other in any + // policy or are a mix of indices from 2 or more policies + final List unfilteredPolicy = randomFrom(allFilteredIndices); + List _unfilteredIndices; + if (randomBoolean()) { + _unfilteredIndices = randomListFromLengthBetween(unfilteredPolicy, 0, unfilteredPolicy.size()); + // add indices distinct from any index in any filter policy + for (int i = 0; i < randomIntBetween(1, 4); i++) { + _unfilteredIndices.add(UNFILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + } + } else { + _unfilteredIndices = randomListFromLengthBetween(unfilteredPolicy, 1, unfilteredPolicy.size()); + // add indices from other filter policies + final List otherIndices = new ArrayList<>(); + for (int j = 0; j < randomIntBetween(1, 4); j++) { + otherIndices.add(FILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + } + _unfilteredIndices.addAll(randomListFromLengthBetween(otherIndices, 1, otherIndices.size())); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters.otherPolicy.indices", otherIndices); + } + final String[] unfilteredIndices = _unfilteredIndices.toArray(new String[0]); + User user; + if (randomBoolean()) { + user = new User("user1", new String[] { "r1" }, new User("authUsername", new String[] { "r2" })); + } else { + user = new User("user1", new String[] { "r1" }); + } + final MockToken authToken = new MockToken("token1"); + final TransportMessage noIndexMessage = new MockMessage(threadContext); + + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + // anonymous accessDenied + auditTrail.anonymousAccessDenied("_action", noIndexMessage); + if (filterMissingIndices) { + assertThat("Anonymous message no index: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous message no index: filtered out by indices filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied("_action", new MockIndicesRequest(threadContext, unfilteredIndices)); + assertThat("Anonymous message unfiltered indices: filtered out by indices filters", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied("_action", new MockIndicesRequest(threadContext, filteredIndices)); + assertThat("Anonymous message filtered indices: not filtered out by indices filters", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.anonymousAccessDenied(getRestRequest()); + if (filterMissingIndices) { + assertThat("Anonymous rest request: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("Anonymous rest request: filtered out by indices filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authenticationFailed + auditTrail.authenticationFailed(getRestRequest()); + if (filterMissingIndices) { + assertThat("AuthenticationFailed no token rest request: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AuthenticationFailed no token rest request: filtered out by indices filters", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, "_action", noIndexMessage); + if (filterMissingIndices) { + assertThat("AuthenticationFailed token request no index: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AuthenticationFailed token request no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, "_action", new MockIndicesRequest(threadContext, unfilteredIndices)); + assertThat("AuthenticationFailed token request unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, "_action", new MockIndicesRequest(threadContext, filteredIndices)); + assertThat("AuthenticationFailed token request filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", noIndexMessage); + if (filterMissingIndices) { + assertThat("AuthenticationFailed no token message no index: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AuthenticationFailed no token message: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", new MockIndicesRequest(threadContext, unfilteredIndices)); + assertThat("AuthenticationFailed no token request unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_action", new MockIndicesRequest(threadContext, filteredIndices)); + assertThat("AuthenticationFailed no token request filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed(authToken, getRestRequest()); + if (filterMissingIndices) { + assertThat("AuthenticationFailed rest request: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed rest request: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, "_action", noIndexMessage); + if (filterMissingIndices) { + assertThat("AuthenticationFailed realm message no index: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AuthenticationFailed realm message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, "_action", new MockIndicesRequest(threadContext, unfilteredIndices)); + assertThat("AuthenticationFailed realm message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, "_action", new MockIndicesRequest(threadContext, filteredIndices)); + assertThat("AuthenticationFailed realm message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationFailed("_realm", authToken, getRestRequest()); + if (filterMissingIndices) { + assertThat("AuthenticationFailed realm rest request: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationFailed realm rest request: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // accessGranted + auditTrail.accessGranted(user, "_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("AccessGranted message no index: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AccessGranted message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "_action", new MockIndicesRequest(threadContext, unfilteredIndices), new String[] { "role1" }); + assertThat("AccessGranted message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(user, "_action", new MockIndicesRequest(threadContext, filteredIndices), new String[] { "role1" }); + assertThat("AccessGranted message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("AccessGranted message system user no index: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AccessGranted message system user no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", new MockIndicesRequest(threadContext, unfilteredIndices), + new String[] { "role1" }); + assertThat("AccessGranted message system user unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", new MockIndicesRequest(threadContext, filteredIndices), + new String[] { "role1" }); + assertThat("AccessGranted message system user filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // accessDenied + auditTrail.accessDenied(user, "_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("AccessDenied message no index: not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AccessDenied message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "_action", new MockIndicesRequest(threadContext, unfilteredIndices), new String[] { "role1" }); + assertThat("AccessDenied message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(user, "_action", new MockIndicesRequest(threadContext, filteredIndices), new String[] { "role1" }); + assertThat("AccessDenied message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("AccessDenied message system user no index: not filtered out by the missing indices filter", logOutput.size(), + is(0)); + } else { + assertThat("AccessDenied message system user no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", new MockIndicesRequest(threadContext, unfilteredIndices), + new String[] { "role1" }); + assertThat("AccessDenied message system user unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.accessDenied(SystemUser.INSTANCE, "internal:_action", new MockIndicesRequest(threadContext, filteredIndices), + new String[] { "role1" }); + assertThat("AccessGranted message system user filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // connection denied + auditTrail.connectionDenied(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingIndices) { + assertThat("Connection denied: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("Connection denied: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // connection granted + auditTrail.connectionGranted(InetAddress.getLoopbackAddress(), "default", new SecurityIpFilterRule(false, "_all")); + if (filterMissingIndices) { + assertThat("Connection granted: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("Connection granted: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // runAsGranted + auditTrail.runAsGranted(user, "_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("RunAsGranted message no index: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsGranted message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsGranted(user, "_action", new MockIndicesRequest(threadContext, unfilteredIndices), new String[] { "role1" }); + assertThat("RunAsGranted message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsGranted(user, "_action", new MockIndicesRequest(threadContext, filteredIndices), new String[] { "role1" }); + assertThat("RunAsGranted message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + // runAsDenied + auditTrail.runAsDenied(user, "_action", noIndexMessage, new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("RunAsDenied message no index: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsDenied message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, "_action", new MockIndicesRequest(threadContext, unfilteredIndices), new String[] { "role1" }); + assertThat("RunAsDenied message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, "_action", new MockIndicesRequest(threadContext, filteredIndices), new String[] { "role1" }); + assertThat("RunAsDenied message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.runAsDenied(user, getRestRequest(), new String[] { "role1" }); + if (filterMissingIndices) { + assertThat("RunAsDenied rest request: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("RunAsDenied rest request: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + // authentication Success + auditTrail.authenticationSuccess("_realm", user, getRestRequest()); + if (filterMissingIndices) { + assertThat("AuthenticationSuccess rest request: is not filtered out by the missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationSuccess rest request: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", user, "_action", noIndexMessage); + if (filterMissingIndices) { + assertThat("AuthenticationSuccess message no index: not filtered out by missing indices filter", logOutput.size(), is(0)); + } else { + assertThat("AuthenticationSuccess message no index: filtered out by indices filter", logOutput.size(), is(1)); + } + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", user, "_action", new MockIndicesRequest(threadContext, unfilteredIndices)); + assertThat("AuthenticationSuccess message unfiltered indices: filtered out by indices filter", logOutput.size(), is(1)); + logOutput.clear(); + threadContext.stashContext(); + + auditTrail.authenticationSuccess("_realm", user, "_action", new MockIndicesRequest(threadContext, filteredIndices)); + assertThat("AuthenticationSuccess message filtered indices: not filtered out by indices filter", logOutput.size(), is(0)); + logOutput.clear(); + threadContext.stashContext(); + } + + private List randomListFromLengthBetween(List l, int min, int max) { + assert min >= 0 && min <= max && max <= l.size(); + final int len = randomIntBetween(min, max); + final List ans = new ArrayList<>(len); + while (ans.size() < len) { + ans.add(randomFrom(l)); + } + return ans; + } + + private List randomNonEmptyListOfFilteredNames() { + final List filtered = new ArrayList<>(4); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + filtered.add(FILTER_MARKER + randomAlphaOfLengthBetween(1, 4)); + } + return filtered; + } + + private RestRequest getRestRequest() throws IOException { + final RestContent content = randomFrom(RestContent.values()); + final FakeRestRequest.Builder builder = new Builder(NamedXContentRegistry.EMPTY); + if (content.hasContent()) { + builder.withContent(content.content(), XContentType.JSON); + } + builder.withPath("_uri"); + final byte address[] = InetAddress.getByName(randomBoolean() ? "127.0.0.1" : "::1").getAddress(); + builder.withRemoteAddress(new InetSocketAddress(InetAddress.getByAddress("_hostname", address), 9200)); + builder.withParams(Collections.emptyMap()); + return builder.build(); + } + + private static class MockToken implements AuthenticationToken { + private final String principal; + + MockToken(String principal) { + this.principal = principal; + } + + @Override + public String principal() { + return this.principal; + } + + @Override + public Object credentials() { + fail("it's not allowed to print the credentials of the auth token"); + return null; + } + + @Override + public void clearCredentials() { + + } + } + + static class MockIndicesRequest extends org.elasticsearch.action.MockIndicesRequest { + + MockIndicesRequest(ThreadContext threadContext, String... indices) throws IOException { + super(IndicesOptions.strictExpandOpenAndForbidClosed(), indices); + if (randomBoolean()) { + remoteAddress(buildNewFakeTransportAddress()); + } + if (randomBoolean()) { + RemoteHostHeader.putRestRemoteAddress(threadContext, new InetSocketAddress(forge("localhost", "127.0.0.1"), 1234)); + } + } + + /** creates address without any lookups. hostname can be null, for missing */ + private InetAddress forge(String hostname, String address) throws IOException { + final byte bytes[] = InetAddress.getByName(address).getAddress(); + return InetAddress.getByAddress(hostname, bytes); + } + + @Override + public String toString() { + return "mock-message"; + } + } + + +} \ No newline at end of file diff --git a/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java b/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java index ba0252ab337..83f9ca9d726 100644 --- a/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java +++ b/plugin/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java @@ -27,7 +27,6 @@ import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.rest.FakeRestRequest; import org.elasticsearch.test.rest.FakeRestRequest.Builder; import org.elasticsearch.transport.TransportMessage; -import org.elasticsearch.xpack.security.audit.AuditUtil; import org.elasticsearch.xpack.security.authc.AuthenticationToken; import org.elasticsearch.xpack.security.rest.RemoteHostHeader; import org.elasticsearch.xpack.security.transport.filter.IPFilter; @@ -53,7 +52,7 @@ import static org.mockito.Mockito.when; public class LoggingAuditTrailTests extends ESTestCase { - private enum RestContent { + enum RestContent { VALID() { @Override protected boolean hasContent() { @@ -62,12 +61,12 @@ public class LoggingAuditTrailTests extends ESTestCase { @Override protected BytesReference content() { - return new BytesArray("{ \"key\": \"value\"}"); + return new BytesArray("{ \"key\": \"value\" }"); } @Override protected String expectedMessage() { - return "{ \"key\": \"value\"}"; + return "{ \"key\": \"value\" }"; } }, INVALID() { @@ -78,12 +77,12 @@ public class LoggingAuditTrailTests extends ESTestCase { @Override protected BytesReference content() { - return new BytesArray("{ \"key\": \"value\""); + return new BytesArray("{ \"key\": \"value\" "); } @Override protected String expectedMessage() { - return "{ \"key\": \"value\""; + return "{ \"key\": \"value\" "; } }, EMPTY() { @@ -129,7 +128,7 @@ public class LoggingAuditTrailTests extends ESTestCase { clusterService = mock(ClusterService.class); when(clusterService.localNode()).thenReturn(localNode); Mockito.doAnswer((Answer) invocation -> { - LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0]; + final LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0]; arg0.updateLocalNodeInfo(localNode); return null; }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); @@ -138,10 +137,10 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAnonymousAccessDeniedTransport() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.anonymousAccessDenied("_action", message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [anonymous_access_denied]\t" + origins + @@ -160,11 +159,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAnonymousAccessDeniedRest() throws Exception { - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.anonymousAccessDenied(request); if (includeRequestBody) { @@ -184,10 +183,10 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailed() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.authenticationFailed(new MockToken(), "_action", message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins + @@ -207,10 +206,10 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailedNoToken() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.authenticationFailed("_action", message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_failed]\t" + origins + @@ -229,11 +228,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailedRest() throws Exception { - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.authenticationFailed(new MockToken(), request); if (includeRequestBody) { @@ -254,11 +253,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailedRestNoToken() throws Exception { - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.authenticationFailed(request); if (includeRequestBody) { @@ -278,9 +277,9 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailedRealm() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message); assertEmptyLog(logger); @@ -288,7 +287,7 @@ public class LoggingAuditTrailTests extends ESTestCase { settings = Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "realm_authentication_failed").build(); auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.authenticationFailed("_realm", new MockToken(), "_action", message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [realm_authentication_failed]\trealm=[_realm], " + origins + @@ -301,11 +300,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationFailedRealmRest() throws Exception { - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.authenticationFailed("_realm", new MockToken(), request); assertEmptyLog(logger); @@ -326,20 +325,20 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAccessGranted() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - boolean runAs = randomBoolean(); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final boolean runAs = randomBoolean(); User user; if (runAs) { user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); } else { user = new User("_username", new String[]{"r1"}); } - String role = randomAlphaOfLengthBetween(1, 6); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.accessGranted(user, "_action", message, new String[] { role }); - String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + ", roles=[" + role + "]"; if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo + @@ -358,17 +357,17 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAccessGrantedInternalSystemAction() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String role = randomAlphaOfLengthBetween(1, 6); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role }); assertEmptyLog(logger); // test enabled settings = Settings.builder().put(settings).put("xpack.security.audit.logfile.events.include", "system_access_granted").build(); auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.accessGranted(SystemUser.INSTANCE, "internal:_action", message, new String[] { role }); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", principal=[" + @@ -382,20 +381,20 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAccessGrantedInternalSystemActionNonSystemUser() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - boolean runAs = randomBoolean(); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final boolean runAs = randomBoolean(); User user; if (runAs) { user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); } else { user = new User("_username", new String[]{"r1"}); } - String role = randomAlphaOfLengthBetween(1, 6); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.accessGranted(user, "internal:_action", message, new String[] { role }); - String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + ", roles=[" + role + "]"; if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [access_granted]\t" + origins + ", " + userInfo + @@ -414,20 +413,20 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAccessDenied() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - boolean runAs = randomBoolean(); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final boolean runAs = randomBoolean(); User user; if (runAs) { user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); } else { user = new User("_username", new String[]{"r1"}); } - String role = randomAlphaOfLengthBetween(1, 6); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.accessDenied(user, "_action", message, new String[] { role }); - String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + + final String userInfo = (runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]") + ", roles=[" + role + "]"; if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [access_denied]\t" + origins + ", " + userInfo + @@ -446,11 +445,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testTamperedRequestRest() throws Exception { - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200)); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.tamperedRequest(request); if (includeRequestBody) { @@ -470,11 +469,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testTamperedRequest() throws Exception { - String action = "_action"; - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); - LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final String action = "_action"; + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.tamperedRequest(action, message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins + @@ -489,7 +488,7 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testTamperedRequestWithUser() throws Exception { - String action = "_action"; + final String action = "_action"; final boolean runAs = randomBoolean(); User user; if (runAs) { @@ -497,11 +496,11 @@ public class LoggingAuditTrailTests extends ESTestCase { } else { user = new User("_username", new String[]{"r1"}); } - String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); auditTrail.tamperedRequest(user, action, message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [tampered_request]\t" + origins + ", " + userInfo + @@ -520,10 +519,10 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testConnectionDenied() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - InetAddress inetAddress = InetAddress.getLoopbackAddress(); - SecurityIpFilterRule rule = new SecurityIpFilterRule(false, "_all"); + final InetAddress inetAddress = InetAddress.getLoopbackAddress(); + final SecurityIpFilterRule rule = new SecurityIpFilterRule(false, "_all"); auditTrail.connectionDenied(inetAddress, "default", rule); assertMsg(logger, Level.INFO, String.format(Locale.ROOT, prefix + "[ip_filter] [connection_denied]\torigin_address=[%s], transport_profile=[%s], rule=[deny %s]", @@ -538,10 +537,10 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testConnectionGranted() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - InetAddress inetAddress = InetAddress.getLoopbackAddress(); - SecurityIpFilterRule rule = IPFilter.DEFAULT_PROFILE_ACCEPT_ALL; + final InetAddress inetAddress = InetAddress.getLoopbackAddress(); + final SecurityIpFilterRule rule = IPFilter.DEFAULT_PROFILE_ACCEPT_ALL; auditTrail.connectionGranted(inetAddress, "default", rule); assertEmptyLog(logger); @@ -555,15 +554,22 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testRunAsGranted() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = new MockMessage(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); - String role = randomAlphaOfLengthBetween(1, 6); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.runAsGranted(user, "_action", message, new String[] { role }); - assertMsg(logger, Level.INFO, prefix + "[transport] [run_as_granted]\t" + origins + - ", principal=[_username], run_as_principal=[running as], roles=[" + role + "], action=[_action], request=[MockMessage]"); + if (message instanceof IndicesRequest) { + assertMsg(logger, Level.INFO, + prefix + "[transport] [run_as_granted]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=[" + + role + "], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]"); + } else { + assertMsg(logger, Level.INFO, + prefix + "[transport] [run_as_granted]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=[" + + role + "], action=[_action], request=[MockMessage]"); + } // test disabled CapturingLogger.output(logger.getName(), Level.INFO).clear(); @@ -574,15 +580,22 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testRunAsDenied() throws Exception { - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = new MockMessage(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); - String role = randomAlphaOfLengthBetween(1, 6); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final User user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); + final String role = randomAlphaOfLengthBetween(1, 6); auditTrail.runAsDenied(user, "_action", message, new String[] { role }); - assertMsg(logger, Level.INFO, prefix + "[transport] [run_as_denied]\t" + origins + - ", principal=[_username], run_as_principal=[running as], roles=[" + role + "], action=[_action], request=[MockMessage]"); + if (message instanceof IndicesRequest) { + assertMsg(logger, Level.INFO, + prefix + "[transport] [run_as_denied]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=[" + + role + "], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]"); + } else { + assertMsg(logger, Level.INFO, + prefix + "[transport] [run_as_denied]\t" + origins + ", principal=[_username], run_as_principal=[running as], roles=[" + + role + "], action=[_action], request=[MockMessage]"); + } // test disabled CapturingLogger.output(logger.getName(), Level.INFO).clear(); @@ -593,17 +606,16 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testOriginAttributes() throws Exception { - - MockMessage message = new MockMessage(threadContext); - LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - String text = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext); + final MockMessage message = new MockMessage(threadContext); + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + final String text = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final InetSocketAddress restAddress = RemoteHostHeader.restRemoteAddress(threadContext); if (restAddress != null) { assertThat(text, equalTo("origin_type=[rest], origin_address=[" + NetworkAddress.format(restAddress.getAddress()) + "]")); return; } - TransportAddress address = message.remoteAddress(); + final TransportAddress address = message.remoteAddress(); if (address == null) { assertThat(text, equalTo("origin_type=[local_node], origin_address=[" + localNode.getHostAddress() + "]")); return; @@ -614,26 +626,26 @@ public class LoggingAuditTrailTests extends ESTestCase { } public void testAuthenticationSuccessRest() throws Exception { - Map params = new HashMap<>(); + final Map params = new HashMap<>(); params.put("foo", "bar"); - InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); - Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200), params); - String expectedMessage = tuple.v1().expectedMessage(); - RestRequest request = tuple.v2(); - boolean runAs = randomBoolean(); + final InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + final Tuple tuple = prepareRestContent("_uri", new InetSocketAddress(address, 9200), params); + final String expectedMessage = tuple.v1().expectedMessage(); + final RestRequest request = tuple.v2(); + final boolean runAs = randomBoolean(); User user; if (runAs) { user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); } else { user = new User("_username", new String[] { "r1" }); } - String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; - String realm = "_realm"; + final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; + final String realm = "_realm"; Settings settings = Settings.builder().put(this.settings) .put("xpack.security.audit.logfile.events.include", "authentication_success") .build(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); auditTrail.authenticationSuccess(realm, user, request); if (includeRequestBody) { @@ -657,23 +669,23 @@ public class LoggingAuditTrailTests extends ESTestCase { public void testAuthenticationSuccessTransport() throws Exception { Settings settings = Settings.builder().put(this.settings) .put("xpack.security.audit.logfile.events.include", "authentication_success").build(); - Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); + final Logger logger = CapturingLogger.newCapturingLogger(Level.INFO); LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); - TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); - String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); - boolean runAs = randomBoolean(); + final TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + final String origins = LoggingAuditTrail.originAttributes(threadContext, message, auditTrail.localNodeInfo); + final boolean runAs = randomBoolean(); User user; if (runAs) { user = new User("running as", new String[]{"r2"}, new User("_username", new String[] {"r1"})); } else { user = new User("_username", new String[] { "r1" }); } - String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; - String realm = "_realm"; + final String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; + final String realm = "_realm"; auditTrail.authenticationSuccess(realm, user, "_action", message); if (message instanceof IndicesRequest) { assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo - + ", realm=[_realm], action=[_action], request=[MockIndicesRequest]"); + + ", realm=[_realm], action=[_action], indices=[" + indices(message) + "], request=[MockIndicesRequest]"); } else { assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo + ", realm=[_realm], action=[_action], request=[MockMessage]"); @@ -691,7 +703,7 @@ public class LoggingAuditTrailTests extends ESTestCase { } private void assertMsg(Logger logger, Level level, String message) { - List output = CapturingLogger.output(logger.getName(), level); + final List output = CapturingLogger.output(logger.getName(), level); assertThat(output.size(), is(1)); assertThat(output.get(0), equalTo(message)); } @@ -700,13 +712,13 @@ public class LoggingAuditTrailTests extends ESTestCase { assertThat(CapturingLogger.isEmpty(logger.getName()), is(true)); } - private Tuple prepareRestContent(String uri, InetSocketAddress remoteAddress) { + protected Tuple prepareRestContent(String uri, InetSocketAddress remoteAddress) { return prepareRestContent(uri, remoteAddress, Collections.emptyMap()); } private Tuple prepareRestContent(String uri, InetSocketAddress remoteAddress, Map params) { - RestContent content = randomFrom(RestContent.values()); - FakeRestRequest.Builder builder = new Builder(NamedXContentRegistry.EMPTY); + final RestContent content = randomFrom(RestContent.values()); + final FakeRestRequest.Builder builder = new Builder(NamedXContentRegistry.EMPTY); if (content.hasContent()) { builder.withContent(content.content(), XContentType.JSON); } @@ -717,18 +729,18 @@ public class LoggingAuditTrailTests extends ESTestCase { } /** creates address without any lookups. hostname can be null, for missing */ - private static InetAddress forge(String hostname, String address) throws IOException { - byte bytes[] = InetAddress.getByName(address).getAddress(); + protected static InetAddress forge(String hostname, String address) throws IOException { + final byte bytes[] = InetAddress.getByName(address).getAddress(); return InetAddress.getByAddress(hostname, bytes); } private static String indices(TransportMessage message) { - return Strings.collectionToCommaDelimitedString(AuditUtil.indices(message)); + return Strings.arrayToCommaDelimitedString(((IndicesRequest) message).indices()); } - private static class MockMessage extends TransportMessage { + static class MockMessage extends TransportMessage { - private MockMessage(ThreadContext threadContext) throws IOException { + MockMessage(ThreadContext threadContext) throws IOException { if (randomBoolean()) { if (randomBoolean()) { remoteAddress(buildNewFakeTransportAddress()); @@ -742,9 +754,9 @@ public class LoggingAuditTrailTests extends ESTestCase { } } - private static class MockIndicesRequest extends org.elasticsearch.action.MockIndicesRequest { + static class MockIndicesRequest extends org.elasticsearch.action.MockIndicesRequest { - private MockIndicesRequest(ThreadContext threadContext) throws IOException { + MockIndicesRequest(ThreadContext threadContext) throws IOException { super(IndicesOptions.strictExpandOpenAndForbidClosed(), "idx1", "idx2"); if (randomBoolean()) { remoteAddress(buildNewFakeTransportAddress());