diff --git a/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index f21f9ec370c..379afe72cbc 100644 --- a/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -12,6 +12,7 @@ import org.elasticsearch.cluster.ClusterStateListener; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.network.NetworkAddress; @@ -39,10 +40,10 @@ 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.TreeMap; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -112,7 +113,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, private final EnumSet events; private final boolean includeRequestBody; // protected for testing - protected final Predicate filterPolicyPredicate; + final EventFilterPolicyRegistry eventFilterPolicyRegistry; private final ThreadContext threadContext; volatile LocalNodeInfo localNodeInfo; @@ -132,14 +133,38 @@ 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(); + this.eventFilterPolicyRegistry = new EventFilterPolicyRegistry(settings); clusterService.addListener(this); + clusterService.getClusterSettings().addAffixUpdateConsumer(FILTER_POLICY_IGNORE_PRINCIPALS, (policyName, filtersList) -> { + final Optional policy = eventFilterPolicyRegistry.get(policyName); + final EventFilterPolicy newPolicy = policy.orElse(new EventFilterPolicy(policyName, settings)) + .changePrincipalsFilter(filtersList); + this.eventFilterPolicyRegistry.set(policyName, newPolicy); + }, (policyName, filtersList) -> EventFilterPolicy.parsePredicate(filtersList)); + clusterService.getClusterSettings().addAffixUpdateConsumer(FILTER_POLICY_IGNORE_REALMS, (policyName, filtersList) -> { + final Optional policy = eventFilterPolicyRegistry.get(policyName); + final EventFilterPolicy newPolicy = policy.orElse(new EventFilterPolicy(policyName, settings)) + .changeRealmsFilter(filtersList); + this.eventFilterPolicyRegistry.set(policyName, newPolicy); + }, (policyName, filtersList) -> EventFilterPolicy.parsePredicate(filtersList)); + clusterService.getClusterSettings().addAffixUpdateConsumer(FILTER_POLICY_IGNORE_ROLES, (policyName, filtersList) -> { + final Optional policy = eventFilterPolicyRegistry.get(policyName); + final EventFilterPolicy newPolicy = policy.orElse(new EventFilterPolicy(policyName, settings)) + .changeRolesFilter(filtersList); + this.eventFilterPolicyRegistry.set(policyName, newPolicy); + }, (policyName, filtersList) -> EventFilterPolicy.parsePredicate(filtersList)); + clusterService.getClusterSettings().addAffixUpdateConsumer(FILTER_POLICY_IGNORE_INDICES, (policyName, filtersList) -> { + final Optional policy = eventFilterPolicyRegistry.get(policyName); + final EventFilterPolicy newPolicy = policy.orElse(new EventFilterPolicy(policyName, settings)) + .changeIndicesFilter(filtersList); + this.eventFilterPolicyRegistry.set(policyName, newPolicy); + }, (policyName, filtersList) -> EventFilterPolicy.parsePredicate(filtersList)); } @Override public void authenticationSuccess(String realm, User user, RestRequest request) { - if (events.contains(AUTHENTICATION_SUCCESS) && filterPolicyPredicate - .test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), Optional.empty())) == false) { + if (events.contains(AUTHENTICATION_SUCCESS) && (eventFilterPolicyRegistry.ignorePredicate() + .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)); @@ -154,7 +179,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { if (events.contains(AUTHENTICATION_SUCCESS)) { final Optional indices = indices(message); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.of(realm), Optional.empty(), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -174,7 +199,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void anonymousAccessDenied(String action, TransportMessage message) { if (events.contains(ANONYMOUS_ACCESS_DENIED)) { final Optional indices = indices(message); - if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + if (eventFilterPolicyRegistry.ignorePredicate() + .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=[{}]", @@ -190,7 +216,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void anonymousAccessDenied(RestRequest request) { - if (events.contains(ANONYMOUS_ACCESS_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + if (events.contains(ANONYMOUS_ACCESS_DENIED) + && (eventFilterPolicyRegistry.ignorePredicate().test(AuditEventMetaInfo.EMPTY) == false)) { if (includeRequestBody) { logger.info("{}[rest] [anonymous_access_denied]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri(), restRequestContent(request)); @@ -205,7 +232,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void authenticationFailed(AuthenticationToken token, String action, TransportMessage message) { if (events.contains(AUTHENTICATION_FAILED)) { final Optional indices = indices(message); - if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), indices)) == false) { + if (eventFilterPolicyRegistry.ignorePredicate() + .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=[{}]", @@ -222,7 +250,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(RestRequest request) { - if (events.contains(AUTHENTICATION_FAILED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + if (events.contains(AUTHENTICATION_FAILED) + && (eventFilterPolicyRegistry.ignorePredicate().test(AuditEventMetaInfo.EMPTY) == false)) { if (includeRequestBody) { logger.info("{}[rest] [authentication_failed]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri(), restRequestContent(request)); @@ -236,7 +265,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void authenticationFailed(String action, TransportMessage message) { if (events.contains(AUTHENTICATION_FAILED)) { final Optional indices = indices(message); - if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + if (eventFilterPolicyRegistry.ignorePredicate() + .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, @@ -253,7 +283,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(AuthenticationToken token, RestRequest request) { if (events.contains(AUTHENTICATION_FAILED) - && filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.empty(), Optional.empty())) == false) { + && (eventFilterPolicyRegistry.ignorePredicate() + .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)); @@ -268,7 +299,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void authenticationFailed(String realm, AuthenticationToken token, String action, TransportMessage message) { if (events.contains(REALM_AUTHENTICATION_FAILED)) { final Optional indices = indices(message); - if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), indices)) == false) { + if (eventFilterPolicyRegistry.ignorePredicate() + .test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { logger.info( @@ -287,8 +319,9 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void authenticationFailed(String realm, AuthenticationToken token, RestRequest request) { - if (events.contains(REALM_AUTHENTICATION_FAILED) && filterPolicyPredicate - .test(new AuditEventMetaInfo(Optional.of(token), Optional.of(realm), Optional.empty())) == false) { + if (events.contains(REALM_AUTHENTICATION_FAILED) + && (eventFilterPolicyRegistry.ignorePredicate() + .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(), @@ -303,9 +336,9 @@ 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); - if ((isSystem && events.contains(SYSTEM_ACCESS_GRANTED)) || (isSystem == false && events.contains(ACCESS_GRANTED))) { + if ((isSystem && events.contains(SYSTEM_ACCESS_GRANTED)) || ((isSystem == false) && events.contains(ACCESS_GRANTED))) { final Optional indices = indices(message); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -326,7 +359,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void accessDenied(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(ACCESS_DENIED)) { final Optional indices = indices(message); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -345,7 +378,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void tamperedRequest(RestRequest request) { - if (events.contains(TAMPERED_REQUEST) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + if (events.contains(TAMPERED_REQUEST) && (eventFilterPolicyRegistry.ignorePredicate().test(AuditEventMetaInfo.EMPTY) == false)) { if (includeRequestBody) { logger.info("{}[rest] [tampered_request]\t{}, uri=[{}], request_body=[{}]", localNodeInfo.prefix, hostAttributes(request), request.uri(), restRequestContent(request)); @@ -359,7 +392,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void tamperedRequest(String action, TransportMessage message) { if (events.contains(TAMPERED_REQUEST)) { final Optional indices = indices(message); - if (filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.empty(), Optional.empty(), indices)) == false) { + if (eventFilterPolicyRegistry.ignorePredicate() + .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, @@ -377,7 +411,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void tamperedRequest(User user, String action, TransportMessage request) { if (events.contains(TAMPERED_REQUEST)) { final Optional indices = indices(request); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.empty(), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -395,7 +429,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void connectionGranted(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { - if (events.contains(CONNECTION_GRANTED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + if (events.contains(CONNECTION_GRANTED) && (eventFilterPolicyRegistry.ignorePredicate().test(AuditEventMetaInfo.EMPTY) == false)) { logger.info("{}[ip_filter] [connection_granted]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); } @@ -403,7 +437,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void connectionDenied(InetAddress inetAddress, String profile, SecurityIpFilterRule rule) { - if (events.contains(CONNECTION_DENIED) && filterPolicyPredicate.test(AuditEventMetaInfo.EMPTY) == false) { + if (events.contains(CONNECTION_DENIED) && (eventFilterPolicyRegistry.ignorePredicate().test(AuditEventMetaInfo.EMPTY) == false)) { logger.info("{}[ip_filter] [connection_denied]\torigin_address=[{}], transport_profile=[{}], rule=[{}]", localNodeInfo.prefix, NetworkAddress.format(inetAddress), profile, rule); } @@ -413,7 +447,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void runAsGranted(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(RUN_AS_GRANTED)) { final Optional indices = indices(message); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -439,7 +473,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, public void runAsDenied(User user, String action, TransportMessage message, String[] roleNames) { if (events.contains(RUN_AS_DENIED)) { final Optional indices = indices(message); - if (filterPolicyPredicate + if (eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), indices)) == false) { final LocalNodeInfo localNodeInfo = this.localNodeInfo; if (indices.isPresent()) { @@ -463,8 +497,8 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, @Override public void runAsDenied(User user, RestRequest request, String[] roleNames) { - if (events.contains(RUN_AS_DENIED) && filterPolicyPredicate - .test(new AuditEventMetaInfo(Optional.of(user), Optional.empty(), Optional.of(roleNames), Optional.empty())) == false) { + if (events.contains(RUN_AS_DENIED) && (eventFilterPolicyRegistry.ignorePredicate() + .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(), @@ -552,27 +586,55 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, * the policy. */ private static final class EventFilterPolicy { - final String name; + private final String name; private final Predicate ignorePrincipalsPredicate; private final Predicate ignoreRealmsPredicate; private final Predicate ignoreRolesPredicate; private final Predicate ignoreIndicesPredicate; + EventFilterPolicy(String name, Settings settings) { + this(name, parsePredicate(FILTER_POLICY_IGNORE_PRINCIPALS.getConcreteSettingForNamespace(name).get(settings)), + parsePredicate(FILTER_POLICY_IGNORE_REALMS.getConcreteSettingForNamespace(name).get(settings)), + parsePredicate(FILTER_POLICY_IGNORE_ROLES.getConcreteSettingForNamespace(name).get(settings)), + parsePredicate(FILTER_POLICY_IGNORE_INDICES.getConcreteSettingForNamespace(name).get(settings))); + } + /** * 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) { + EventFilterPolicy(String name, Predicate ignorePrincipalsPredicate, Predicate ignoreRealmsPredicate, + Predicate ignoreRolesPredicate, Predicate ignoreIndicesPredicate) { 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))); + this.ignorePrincipalsPredicate = ignorePrincipalsPredicate; + this.ignoreRealmsPredicate = ignoreRealmsPredicate; + this.ignoreRolesPredicate = ignoreRolesPredicate; + this.ignoreIndicesPredicate = ignoreIndicesPredicate; + } + + private EventFilterPolicy changePrincipalsFilter(List filtersList) { + return new EventFilterPolicy(name, parsePredicate(filtersList), ignoreRealmsPredicate, ignoreRolesPredicate, + ignoreIndicesPredicate); + } + + private EventFilterPolicy changeRealmsFilter(List filtersList) { + return new EventFilterPolicy(name, ignorePrincipalsPredicate, parsePredicate(filtersList), ignoreRolesPredicate, + ignoreIndicesPredicate); + } + + private EventFilterPolicy changeRolesFilter(List filtersList) { + return new EventFilterPolicy(name, ignorePrincipalsPredicate, ignoreRealmsPredicate, parsePredicate(filtersList), + ignoreIndicesPredicate); + } + + private EventFilterPolicy changeIndicesFilter(List filtersList) { + return new EventFilterPolicy(name, ignorePrincipalsPredicate, ignoreRealmsPredicate, ignoreRolesPredicate, + parsePredicate(filtersList)); + } + + static Predicate parsePredicate(List l) { + return Automatons.predicate(emptyStringBuildsEmptyAutomaton(l)); } /** @@ -581,7 +643,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, * `Automatons.predicate("").test("") == false` * `Automatons.predicate("//").test("") == true` */ - private List emptyStringBuildsEmptyAutomaton(List l) { + private static List emptyStringBuildsEmptyAutomaton(List l) { if (l.isEmpty()) { return Collections.singletonList("//"); } @@ -594,12 +656,14 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, * 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); - }; + return eventInfo -> ignorePrincipalsPredicate.test(eventInfo.principal) && ignoreRealmsPredicate.test(eventInfo.realm) + && eventInfo.roles.get().allMatch(ignoreRolesPredicate) && eventInfo.indices.get().allMatch(ignoreIndicesPredicate); + } + + @Override + public String toString() { + return "[users]:" + ignorePrincipalsPredicate.toString() + "&[realms]:" + ignoreRealmsPredicate.toString() + "&[roles]:" + + ignoreRolesPredicate.toString() + "&[indices]:" + ignoreIndicesPredicate.toString(); } } @@ -607,19 +671,46 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, * 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; + static final class EventFilterPolicyRegistry { + private volatile Map policyMap; + private volatile Predicate predicate; - EventFilterPolicyRegistry(Settings settings) { - final Map> map = new HashMap<>(); + private EventFilterPolicyRegistry(Settings settings) { + final MapBuilder mapBuilder = MapBuilder.newMapBuilder(); for (final String policyName : settings.getGroups(FILTER_POLICY_PREFIX, true).keySet()) { - map.put(policyName, new EventFilterPolicy(policyName, settings).ignorePredicate()); + mapBuilder.put(policyName, new EventFilterPolicy(policyName, settings)); } - policyMap = Collections.unmodifiableMap(map); + policyMap = mapBuilder.immutableMap(); + // precompute predicate + predicate = buildIgnorePredicate(policyMap); + } + + private Optional get(String policyName) { + return Optional.ofNullable(policyMap.get(policyName)); + } + + private synchronized void set(String policyName, EventFilterPolicy eventFilterPolicy) { + policyMap = MapBuilder.newMapBuilder(policyMap).put(policyName, eventFilterPolicy).immutableMap(); + // precompute predicate + predicate = buildIgnorePredicate(policyMap); } Predicate ignorePredicate() { - return policyMap.values().stream().reduce(x -> false, (x, y) -> x.or(y)); + return predicate; + } + + private static Predicate buildIgnorePredicate(Map policyMap) { + return policyMap.values().stream().map(EventFilterPolicy::ignorePredicate).reduce(x -> false, (x, y) -> x.or(y)); + } + + @Override + public String toString() { + final Map treeMap = new TreeMap<>(policyMap); + final StringBuilder sb = new StringBuilder(); + for (final Map.Entry entry : treeMap.entrySet()) { + sb.append(entry.getKey()).append(":").append(entry.getValue().toString()); + } + return sb.toString(); } } @@ -674,7 +765,7 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail, void updateLocalNodeInfo(DiscoveryNode newLocalNode) { // check if local node changed final DiscoveryNode localNode = localNodeInfo.localNode; - if (localNode == null || localNode.equals(newLocalNode) == false) { + if ((localNode == null) || (localNode.equals(newLocalNode) == false)) { // no need to synchronize, called only from the cluster state applier thread localNodeInfo = new LocalNodeInfo(settings, newLocalNode); } diff --git a/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailFilteringUpdateTests.java b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailFilteringUpdateTests.java new file mode 100644 index 00000000000..70d0cbced7a --- /dev/null +++ b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/AuditTrailFilteringUpdateTests.java @@ -0,0 +1,154 @@ +/* + * 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.elasticsearch.cluster.service.ClusterService; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.test.SecurityIntegTestCase; +import org.elasticsearch.xpack.security.audit.AuditTrailService; +import org.elasticsearch.test.ESIntegTestCase.ClusterScope; +import org.junit.BeforeClass; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import static org.elasticsearch.test.ESIntegTestCase.Scope.TEST; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked; +import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ClusterScope(scope = TEST, numDataNodes = 1) +public class AuditTrailFilteringUpdateTests extends SecurityIntegTestCase { + + private static Settings startupFilterSettings; + private static Settings updateFilterSettings; + + @BeforeClass + public static void startupFilterSettings() { + final Settings.Builder settingsBuilder = Settings.builder(); + // generate random filter policies + for (int i = 0; i < randomIntBetween(0, 4); i++) { + settingsBuilder.put(randomFilterPolicySettings("startupPolicy" + i)); + } + startupFilterSettings = settingsBuilder.build(); + } + + @BeforeClass + public static void updateFilterSettings() { + final Settings.Builder settingsBuilder = Settings.builder(); + // generate random filter policies + for (int i = 0; i < randomIntBetween(1, 4); i++) { + settingsBuilder.put(randomFilterPolicySettings("updatePolicy" + i)); + } + updateFilterSettings = settingsBuilder.build(); + } + + @Override + protected Settings nodeSettings(int nodeOrdinal) { + final Settings.Builder settingsBuilder = Settings.builder(); + settingsBuilder.put(super.nodeSettings(nodeOrdinal)); + + // enable auditing + settingsBuilder.put("xpack.security.audit.enabled", "true"); + settingsBuilder.put("xpack.security.audit.outputs", "logfile"); + // add only startup filter policies + settingsBuilder.put(startupFilterSettings); + return settingsBuilder.build(); + } + + public void testDynamicSettings() throws Exception { + final ClusterService clusterService = mock(ClusterService.class); + final ClusterSettings clusterSettings = mockClusterSettings(); + when(clusterService.getClusterSettings()).thenReturn(clusterSettings); + final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + final Settings.Builder settingsBuilder = Settings.builder(); + settingsBuilder.put(startupFilterSettings); + settingsBuilder.put(updateFilterSettings); + // reference audit trail containing all filters + final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); + final String expected = auditTrail.eventFilterPolicyRegistry.toString(); + // update settings on internal cluster + updateSettings(updateFilterSettings); + final String actual = ((LoggingAuditTrail) internalCluster().getInstances(AuditTrailService.class) + .iterator() + .next() + .getAuditTrails() + .iterator() + .next()).eventFilterPolicyRegistry.toString(); + assertEquals(expected, actual); + } + + public void testInvalidSettings() throws Exception { + final String invalidLuceneRegex = "/invalid"; + final Settings.Builder settingsBuilder = Settings.builder(); + final String[] allSettingsKeys = new String[] { "xpack.security.audit.logfile.events.ignore_filters.invalid.users", + "xpack.security.audit.logfile.events.ignore_filters.invalid.realms", + "xpack.security.audit.logfile.events.ignore_filters.invalid.roles", + "xpack.security.audit.logfile.events.ignore_filters.invalid.indices" }; + settingsBuilder.put(randomFrom(allSettingsKeys), invalidLuceneRegex); + final IllegalArgumentException e = expectThrows(IllegalArgumentException.class, + () -> client().admin().cluster().prepareUpdateSettings().setTransientSettings(settingsBuilder.build()).get()); + assertThat(e.getMessage(), containsString("illegal value can't update")); + } + + private void updateSettings(Settings settings) { + if (randomBoolean()) { + assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings)); + } else { + assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings)); + } + } + + private static List randomNonEmptyListOfFilteredNames(String... namePrefix) { + final List filtered = new ArrayList<>(4); + for (int i = 0; i < randomIntBetween(1, 4); i++) { + filtered.add(Strings.arrayToCommaDelimitedString(namePrefix) + randomAlphaOfLengthBetween(1, 4)); + } + return filtered; + } + + private static Settings randomFilterPolicySettings(String policyName) { + final Settings.Builder settingsBuilder = Settings.builder(); + do { + if (randomBoolean()) { + // filter by username + final List filteredUsernames = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters." + policyName + ".users", filteredUsernames); + } + if (randomBoolean()) { + // filter by realms + final List filteredRealms = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters." + policyName + ".realms", filteredRealms); + } + if (randomBoolean()) { + // filter by roles + final List filteredRoles = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters." + policyName + ".roles", filteredRoles); + } + if (randomBoolean()) { + // filter by indices + final List filteredIndices = randomNonEmptyListOfFilteredNames(); + settingsBuilder.putList("xpack.security.audit.logfile.events.ignore_filters." + policyName + ".indices", filteredIndices); + } + } while (settingsBuilder.build().isEmpty()); + + assertFalse(settingsBuilder.build().isEmpty()); + + return settingsBuilder.build(); + } + + private ClusterSettings mockClusterSettings() { + final List> settingsList = new ArrayList<>(); + LoggingAuditTrail.registerSettings(settingsList); + settingsList.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + return new ClusterSettings(Settings.EMPTY, new HashSet<>(settingsList)); + } +} diff --git a/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java index 6a30e3dc6e8..9528d8c0462 100644 --- a/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java +++ b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java @@ -11,6 +11,8 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.xcontent.NamedXContentRegistry; @@ -38,6 +40,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -71,6 +74,8 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { when(localNode.getHostAddress()).thenReturn(buildNewFakeTransportAddress().toString()); clusterService = mock(ClusterService.class); when(clusterService.localNode()).thenReturn(localNode); + final ClusterSettings clusterSettings = mockClusterSettings(); + when(clusterService.getClusterSettings()).thenReturn(clusterSettings); Mockito.doAnswer((Answer) invocation -> { final LoggingAuditTrail arg0 = (LoggingAuditTrail) invocation.getArguments()[0]; arg0.updateLocalNodeInfo(localNode); @@ -107,7 +112,8 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { final LoggingAuditTrail auditTrail = new LoggingAuditTrail(settingsBuilder.build(), clusterService, logger, threadContext); // all fields match - assertTrue("Matches the filter predicate.", auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo( + assertTrue("Matches the filter predicate.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .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]))))); @@ -120,21 +126,23 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { } // 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), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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(), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)); @@ -142,12 +150,12 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { 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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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<>( @@ -155,12 +163,14 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { 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 + assertFalse("Does not match the filter predicate because of some of the indices.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate() .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 + assertFalse("Does not match the filter predicate because of the empty indices.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), emptyIndices))); @@ -200,7 +210,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { // all fields match assertTrue("Matches the filter predicate.", - auditTrail.filterPolicyPredicate.test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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]))))); @@ -213,21 +223,23 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { } // 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), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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(), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)); @@ -235,12 +247,12 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { 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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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<>( @@ -248,12 +260,13 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { 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 + assertFalse("Does not match the filter predicate because of some of the indices.", + auditTrail.eventFilterPolicyRegistry.ignorePredicate() .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 + assertTrue("Matches the filter predicate because of the empty indices.", auditTrail.eventFilterPolicyRegistry.ignorePredicate() .test(new AuditEventMetaInfo(Optional.of(randomFrom(filteredUsers)), Optional.of(randomFrom(filteredRealms)), Optional.of(randomSubsetOf(randomIntBetween(1, filteredRoles.size()), filteredRoles).toArray(new String[0])), emptyIndices))); @@ -300,25 +313,26 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { } // 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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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)), + auditTrail.eventFilterPolicyRegistry.ignorePredicate().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), + auditTrail.eventFilterPolicyRegistry.ignorePredicate() + .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]))))); @@ -1493,7 +1507,7 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { } private List randomListFromLengthBetween(List l, int min, int max) { - assert min >= 0 && min <= max && max <= l.size(); + assert (min >= 0) && (min <= max) && (max <= l.size()); final int len = randomIntBetween(min, max); final List ans = new ArrayList<>(len); while (ans.size() < len) { @@ -1502,6 +1516,13 @@ public class LoggingAuditTrailFilterTests extends ESTestCase { return ans; } + private ClusterSettings mockClusterSettings() { + final List> settingsList = new ArrayList<>(); + LoggingAuditTrail.registerSettings(settingsList); + settingsList.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + return new ClusterSettings(settings, new HashSet<>(settingsList)); + } + private List randomNonEmptyListOfFilteredNames(String... namePrefix) { final List filtered = new ArrayList<>(4); for (int i = 0; i < randomIntBetween(1, 4); i++) { diff --git a/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java index 56efd820357..bf19fb73850 100644 --- a/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java +++ b/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java @@ -16,6 +16,8 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.network.NetworkAddress; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.util.concurrent.ThreadContext; @@ -40,8 +42,10 @@ 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.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -135,6 +139,8 @@ public class LoggingAuditTrailTests extends ESTestCase { arg0.updateLocalNodeInfo(localNode); return null; }).when(clusterService).addListener(Mockito.isA(LoggingAuditTrail.class)); + final ClusterSettings clusterSettings = mockClusterSettings(); + when(clusterService.getClusterSettings()).thenReturn(clusterSettings); prefix = LoggingAuditTrail.LocalNodeInfo.resolvePrefix(settings, localNode); threadContext = new ThreadContext(Settings.EMPTY); } @@ -794,6 +800,13 @@ public class LoggingAuditTrailTests extends ESTestCase { return Strings.arrayToCommaDelimitedString(((IndicesRequest) message).indices()); } + private ClusterSettings mockClusterSettings() { + final List> settingsList = new ArrayList<>(); + LoggingAuditTrail.registerSettings(settingsList); + settingsList.addAll(ClusterSettings.BUILT_IN_CLUSTER_SETTINGS); + return new ClusterSettings(settings, new HashSet<>(settingsList)); + } + static class MockMessage extends TransportMessage { MockMessage(ThreadContext threadContext) throws IOException {