diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditLevel.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditLevel.java index d1c66a12d87..c8c5fa64200 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditLevel.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditLevel.java @@ -12,6 +12,7 @@ import java.util.Locale; public enum AuditLevel { + ANONYMOUS_ACCESS_DENIED, AUTHENTICATION_FAILED, REALM_AUTHENTICATION_FAILED, @@ -21,6 +22,7 @@ public enum AuditLevel { CONNECTION_GRANTED, CONNECTION_DENIED, SYSTEM_ACCESS_GRANTED, + AUTHENTICATION_SUCCESS, RUN_AS_GRANTED, RUN_AS_DENIED; @@ -59,6 +61,9 @@ public enum AuditLevel { case "system_access_granted": enumSet.add(SYSTEM_ACCESS_GRANTED); break; + case "authentication_success": + enumSet.add(AUTHENTICATION_SUCCESS); + break; case "run_as_granted": enumSet.add(RUN_AS_GRANTED); break; diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrail.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrail.java index e6af432f949..5450d38c3b1 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrail.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrail.java @@ -20,6 +20,10 @@ public interface AuditTrail { String name(); + void authenticationSuccess(String realm, User user, RestRequest request); + + void authenticationSuccess(String realm, User user, String action, TransportMessage message); + void anonymousAccessDenied(String action, TransportMessage message); void anonymousAccessDenied(RestRequest request); diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java index 6caf9108e56..a4561e5440a 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/AuditTrailService.java @@ -48,6 +48,24 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail { return auditTrails; } + @Override + public void authenticationSuccess(String realm, User user, RestRequest request) { + if (licenseState.isAuditingAllowed()) { + for (AuditTrail auditTrail : auditTrails) { + auditTrail.authenticationSuccess(realm, user, request); + } + } + } + + @Override + public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { + if (licenseState.isAuditingAllowed()) { + for (AuditTrail auditTrail : auditTrails) { + auditTrail.authenticationSuccess(realm, user, action, message); + } + } + } + @Override public void anonymousAccessDenied(String action, TransportMessage message) { if (licenseState.isAuditingAllowed()) { diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java index 22727c0008e..9ab4e9fac4b 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java @@ -99,6 +99,7 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_DENIED; import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_GRANTED; import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GRANTED; 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.index.IndexNameResolver.resolve; @@ -135,7 +136,8 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl CONNECTION_GRANTED.toString(), TAMPERED_REQUEST.toString(), RUN_AS_DENIED.toString(), - RUN_AS_GRANTED.toString() + RUN_AS_GRANTED.toString(), + AUTHENTICATION_SUCCESS.toString() ); private static final String FORBIDDEN_INDEX_SETTING = "index.mapper.dynamic"; @@ -311,11 +313,34 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl } } + @Override + public void authenticationSuccess(String realm, User user, RestRequest request) { + if (events.contains(AUTHENTICATION_SUCCESS)) { + try { + enqueue(message("authentication_success", realm, user, request), "authentication_success"); + } catch (Exception e) { + logger.warn("failed to index audit event: [authentication_success]", e); + } + } + } + + @Override + public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { + if (events.contains(AUTHENTICATION_SUCCESS)) { + try { + enqueue(message("authentication_success", action, user, realm, null, message), "authentication_success"); + } catch (Exception e) { + logger.warn("failed to index audit event: [authentication_success]", e); + } + } + } + @Override public void anonymousAccessDenied(String action, TransportMessage message) { if (events.contains(ANONYMOUS_ACCESS_DENIED)) { try { - enqueue(message("anonymous_access_denied", action, null, null, indices(message), message), "anonymous_access_denied"); + enqueue(message("anonymous_access_denied", action, (User) null, null, indices(message), message), + "anonymous_access_denied"); } catch (Exception e) { logger.warn("failed to index audit event: [anonymous_access_denied]", e); } @@ -337,7 +362,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void authenticationFailed(String action, TransportMessage message) { if (events.contains(AUTHENTICATION_FAILED)) { try { - enqueue(message("authentication_failed", action, null, null, indices(message), message), "authentication_failed"); + enqueue(message("authentication_failed", action, (User) null, null, indices(message), message), "authentication_failed"); } catch (Exception e) { logger.warn("failed to index audit event: [authentication_failed]", e); } @@ -413,14 +438,14 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl if ((SystemUser.is(user) && SystemPrivilege.INSTANCE.predicate().test(action))) { if (events.contains(SYSTEM_ACCESS_GRANTED)) { try { - enqueue(message("access_granted", action, user, indices(message), message), "access_granted"); + enqueue(message("access_granted", action, user, null, indices(message), message), "access_granted"); } catch (Exception e) { logger.warn("failed to index audit event: [access_granted]", e); } } } else if (events.contains(ACCESS_GRANTED) && XPackUser.is(user) == false) { try { - enqueue(message("access_granted", action, user, indices(message), message), "access_granted"); + enqueue(message("access_granted", action, user, null, indices(message), message), "access_granted"); } catch (Exception e) { logger.warn("failed to index audit event: [access_granted]", e); } @@ -431,7 +456,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void accessDenied(User user, String action, TransportMessage message) { if (events.contains(ACCESS_DENIED) && XPackUser.is(user) == false) { try { - enqueue(message("access_denied", action, user, indices(message), message), "access_denied"); + enqueue(message("access_denied", action, user, null, indices(message), message), "access_denied"); } catch (Exception e) { logger.warn("failed to index audit event: [access_denied]", e); } @@ -453,7 +478,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void tamperedRequest(String action, TransportMessage message) { if (events.contains(TAMPERED_REQUEST)) { try { - enqueue(message("tampered_request", action, null, indices(message), message), "tampered_request"); + enqueue(message("tampered_request", action, (User) null, null, indices(message), message), "tampered_request"); } catch (Exception e) { logger.warn("failed to index audit event: [tampered_request]", e); } @@ -464,7 +489,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void tamperedRequest(User user, String action, TransportMessage request) { if (events.contains(TAMPERED_REQUEST) && XPackUser.is(user) == false) { try { - enqueue(message("tampered_request", action, user, indices(request), request), "tampered_request"); + enqueue(message("tampered_request", action, user, null, indices(request), request), "tampered_request"); } catch (Exception e) { logger.warn("failed to index audit event: [tampered_request]", e); } @@ -497,7 +522,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void runAsGranted(User user, String action, TransportMessage message) { if (events.contains(RUN_AS_GRANTED)) { try { - enqueue(message("run_as_granted", action, user, null, message), "run_as_granted"); + enqueue(message("run_as_granted", action, user, null, null, message), "run_as_granted"); } catch (Exception e) { logger.warn("failed to index audit event: [run_as_granted]", e); } @@ -508,7 +533,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void runAsDenied(User user, String action, TransportMessage message) { if (events.contains(RUN_AS_DENIED)) { try { - enqueue(message("run_as_denied", action, user, null, message), "run_as_denied"); + enqueue(message("run_as_denied", action, user, null, null, message), "run_as_denied"); } catch (Exception e) { logger.warn("failed to index audit event: [run_as_denied]", e); } @@ -519,14 +544,14 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void runAsDenied(User user, RestRequest request) { if (events.contains(RUN_AS_DENIED)) { try { - enqueue(message("run_as_denied", user, request), "run_as_denied"); + enqueue(message("run_as_denied", null, user, request), "run_as_denied"); } catch (Exception e) { logger.warn("failed to index audit event: [run_as_denied]", e); } } } - private Message message(String type, @Nullable String action, @Nullable User user, + private Message message(String type, @Nullable String action, @Nullable User user, @Nullable String realm, @Nullable Set indices, TransportMessage message) throws Exception { Message msg = new Message().start(); @@ -552,6 +577,9 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl if (indices != null) { msg.builder.array(Field.INDICES, indices.toArray(Strings.EMPTY_ARRAY)); } + if (realm != null) { + msg.builder.field(Field.REALM, realm); + } msg.builder.field(Field.REQUEST, message.getClass().getSimpleName()); return msg.end(); @@ -583,7 +611,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl } private Message message(String type, @Nullable String action, @Nullable AuthenticationToken token, - @Nullable String realm, @Nullable Set indices, RestRequest request) throws Exception { + @Nullable String realm, @Nullable Set indices, RestRequest request) throws Exception { Message msg = new Message().start(); common("rest", type, msg.builder); @@ -614,16 +642,25 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl msg.builder.field(Field.ORIGIN_ADDRESS, address); } msg.builder.field(Field.URI, request.uri()); - return msg.end(); } - private Message message(String type, User user, RestRequest request) throws Exception { + private Message message(String type, String realm, User user, RestRequest request) throws Exception { Message msg = new Message().start(); common("rest", type, msg.builder); - msg.builder.field(Field.PRINCIPAL, user.principal()); + if (user != null) { + if (user.runAs() != null) { + msg.builder.field(Field.PRINCIPAL, user.runAs().principal()); + msg.builder.field(Field.RUN_BY_PRINCIPAL, user.principal()); + } else { + msg.builder.field(Field.PRINCIPAL, user.principal()); + } + } + if (realm != null) { + msg.builder.field(Field.REALM, realm); + } if (includeRequestBody) { msg.builder.field(Field.REQUEST_BODY, restRequestContent(request)); } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index d18df6f560e..0fb877c1445 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -53,6 +53,7 @@ import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_DENIED; import static org.elasticsearch.xpack.security.audit.AuditLevel.RUN_AS_GRANTED; import static org.elasticsearch.xpack.security.audit.AuditLevel.SYSTEM_ACCESS_GRANTED; 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; @@ -119,6 +120,28 @@ public class LoggingAuditTrail extends AbstractComponent implements AuditTrail { return prefix; } + @Override + public void authenticationSuccess(String realm, User user, RestRequest request) { + if (events.contains(AUTHENTICATION_SUCCESS)) { + if (includeRequestBody) { + logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}], request_body=[{}]", getPrefix(), + principal(user), realm, request.uri(), request.params(), restRequestContent(request)); + } else { + logger.info("{}[rest] [authentication_success]\t{}, realm=[{}], uri=[{}], params=[{}]", getPrefix(), principal(user), realm, + request.uri(), request.params()); + } + } + } + + @Override + public void authenticationSuccess(String realm, User user, String action, TransportMessage message) { + if (events.contains(AUTHENTICATION_SUCCESS)) { + logger.info("{}[transport] [authentication_success]\t{}, {}, realm=[{}], action=[{}], request=[{}]", getPrefix(), + originAttributes(message, clusterService.localNode(), threadContext), principal(user), realm, action, + message.getClass().getSimpleName()); + } + } + @Override public void anonymousAccessDenied(String action, TransportMessage message) { if (events.contains(ANONYMOUS_ACCESS_DENIED)) { diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java index b884b9f7699..38bbb7cfaab 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java @@ -153,7 +153,9 @@ public class AuthenticationService extends AbstractComponent { AuthenticationToken token = extractToken(); if (token == null) { - return handleNullToken(); + Authentication authentication = handleNullToken(); + request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), authentication.getUser()); + return authentication; } User user = authenticateToken(token); @@ -165,6 +167,7 @@ public class AuthenticationService extends AbstractComponent { final Authentication authentication = new Authentication(user, authenticatedBy, lookedupBy); authentication.writeToContext(threadContext, cryptoService, signUserHeader); + request.authenticationSuccess(authentication.getAuthenticatedBy().getName(), user); return authentication; } @@ -322,6 +325,8 @@ public class AuthenticationService extends AbstractComponent { abstract ElasticsearchSecurityException anonymousAccessDenied(); abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token); + + abstract void authenticationSuccess(String realm, User user); } class Transport extends AuditableRequest { @@ -334,6 +339,11 @@ public class AuthenticationService extends AbstractComponent { this.message = message; } + @Override + void authenticationSuccess(String realm, User user) { + auditTrail.authenticationSuccess(realm, user, action, message); + } + @Override void realmAuthenticationFailed(AuthenticationToken token, String realm) { auditTrail.authenticationFailed(realm, token, action, message); @@ -373,6 +383,7 @@ public class AuthenticationService extends AbstractComponent { return failureHandler.failedAuthentication(message, token, action, threadContext); } + @Override public String toString() { return "transport request action [" + action + "]"; } @@ -386,6 +397,11 @@ public class AuthenticationService extends AbstractComponent { this.request = request; } + @Override + void authenticationSuccess(String realm, User user) { + auditTrail.authenticationSuccess(realm, user, request); + } + @Override void realmAuthenticationFailed(AuthenticationToken token, String realm) { auditTrail.authenticationFailed(realm, token, request); @@ -425,6 +441,7 @@ public class AuthenticationService extends AbstractComponent { return failureHandler.failedAuthentication(request, token, threadContext); } + @Override public String toString() { return "rest request uri [" + request.uri() + "]"; } diff --git a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java index fcc239a070b..6435745d607 100644 --- a/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java +++ b/elasticsearch/x-pack/security/src/main/java/org/elasticsearch/xpack/security/rest/SecurityRestFilter.java @@ -47,7 +47,7 @@ public class SecurityRestFilter extends RestFilter { @Inject public SecurityRestFilter(AuthenticationService service, RestController controller, Settings settings, - ThreadPool threadPool, XPackLicenseState licenseState, SSLService sslService) { + ThreadPool threadPool, XPackLicenseState licenseState, SSLService sslService) { this.service = service; this.licenseState = licenseState; this.threadContext = threadPool.getThreadContext(); @@ -72,7 +72,7 @@ public class SecurityRestFilter extends RestFilter { if (extractClientCertificate) { putClientCertificateInContext(request, threadContext, logger); } - service.authenticate(request); + service.authenticate(request).getUser(); } RemoteHostHeader.process(request, threadContext); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java index 3b71f34cd58..204b88fa2c3 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/AuditTrailServiceTests.java @@ -192,4 +192,32 @@ public class AuditTrailServiceTests extends ESTestCase { verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()])); } } + + public void testAuthenticationSuccessRest() throws Exception { + User user = new User("_username", "r1"); + String realm = "_realm"; + service.authenticationSuccess(realm, user, restRequest); + verify(licenseState).isAuditingAllowed(); + if (isAuditingAllowed) { + for (AuditTrail auditTrail : auditTrails) { + verify(auditTrail).authenticationSuccess(realm, user, restRequest); + } + } else { + verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()])); + } + } + + public void testAuthenticationSuccessTransport() throws Exception { + User user = new User("_username", "r1"); + String realm = "_realm"; + service.authenticationSuccess(realm, user, "_action", message); + verify(licenseState).isAuditingAllowed(); + if (isAuditingAllowed) { + for (AuditTrail auditTrail : auditTrails) { + verify(auditTrail).authenticationSuccess(realm, user, "_action", message); + } + } else { + verifyZeroInteractions(auditTrails.toArray((Object[]) new AuditTrail[auditTrails.size()])); + } + } } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailMutedTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailMutedTests.java index 9614a9f6019..208ca1ad498 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailMutedTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailMutedTests.java @@ -104,7 +104,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.anonymousAccessDenied(restRequest); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(restRequest); } @@ -122,7 +122,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.authenticationFailed("_action", message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(token, message); } @@ -176,7 +176,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.accessGranted(user, randomAsciiOfLengthBetween(6, 40), message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(message, user); } @@ -187,7 +187,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.accessGranted(user, "internal:foo", message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(message); } @@ -198,7 +198,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.accessDenied(user, randomAsciiOfLengthBetween(6, 40), message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(message, user); } @@ -216,7 +216,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.tamperedRequest(randomAsciiOfLengthBetween(6, 40), message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(message, user); } @@ -228,7 +228,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.connectionGranted(address, randomAsciiOfLengthBetween(1, 12), rule); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(address, rule); } @@ -240,7 +240,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.connectionDenied(address, randomAsciiOfLengthBetween(1, 12), rule); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(address, rule); } @@ -252,7 +252,7 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.runAsGranted(user, randomAsciiOfLengthBetween(6, 40), message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + verifyZeroInteractions(message, user); } @@ -264,7 +264,32 @@ public class IndexAuditTrailMutedTests extends ESTestCase { auditTrail.runAsDenied(user, randomAsciiOfLengthBetween(6, 40), message); assertThat(messageEnqueued.get(), is(false)); assertThat(clientCalled.get(), is(false)); - + + verifyZeroInteractions(message, user); + } + + public void testAuthenticationSuccessRest() { + createAuditTrail(new String[] { "authentication_success" }); + RestRequest restRequest = mock(RestRequest.class); + User user = mock(User.class); + String realm = "_realm"; + + auditTrail.authenticationSuccess(realm, user, restRequest); + assertThat(messageEnqueued.get(), is(false)); + assertThat(clientCalled.get(), is(false)); + + verifyZeroInteractions(restRequest); + } + + public void testAuthenticationSuccessTransport() { + createAuditTrail(new String[] { "authentication_success" }); + TransportMessage message = mock(TransportMessage.class); + User user = mock(User.class); + String realm = "_realm"; + auditTrail.authenticationSuccess(realm, user, randomAsciiOfLengthBetween(6, 40), message); + assertThat(messageEnqueued.get(), is(false)); + assertThat(clientCalled.get(), is(false)); + verifyZeroInteractions(message, user); } diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java index 2cfff86159d..346245c4562 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrailTests.java @@ -53,7 +53,6 @@ import org.elasticsearch.xpack.security.authc.AuthenticationToken; import org.elasticsearch.xpack.security.crypto.CryptoService; import org.elasticsearch.xpack.security.transport.filter.IPFilter; import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule; -import org.elasticsearch.xpack.security.transport.netty3.SecurityNetty3Transport; import org.elasticsearch.xpack.security.user.SystemUser; import org.elasticsearch.xpack.security.user.User; import org.joda.time.DateTime; @@ -628,6 +627,61 @@ public class IndexAuditTrailTests extends SecurityIntegTestCase { assertEquals(sourceMap.get("request"), message.getClass().getSimpleName()); } + public void testAuthenticationSuccessRest() throws Exception { + initialize(); + RestRequest request = mockRestRequest(); + final boolean runAs = randomBoolean(); + User user; + if (runAs) { + user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" })); + } else { + user = new User("_username", new String[] { "r1" }); + } + String realm = "_realm"; + auditor.authenticationSuccess(realm, user, request); + SearchHit hit = getIndexedAuditMessage(enqueuedMessage.get()); + + assertAuditMessage(hit, "rest", "authentication_success"); + Map sourceMap = hit.sourceAsMap(); + assertThat("_uri", equalTo(sourceMap.get("uri"))); + assertRequestBody(sourceMap); + if (runAs) { + assertThat(sourceMap.get("principal"), is("running as")); + assertThat(sourceMap.get("run_by_principal"), is("_username")); + } else { + assertEquals("_username", sourceMap.get("principal")); + } + assertEquals("_realm", sourceMap.get("realm")); + } + + public void testAuthenticationSuccessTransport() throws Exception { + initialize(); + TransportMessage message = randomFrom(new RemoteHostMockMessage(), new LocalHostMockMessage(), new MockIndicesTransportMessage()); + final boolean runAs = randomBoolean(); + User user; + if (runAs) { + user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" })); + } else { + user = new User("_username", new String[] { "r1" }); + } + String realm = "_realm"; + auditor.authenticationSuccess(realm, user, "_action", message); + + SearchHit hit = getIndexedAuditMessage(enqueuedMessage.get()); + Map sourceMap = hit.sourceAsMap(); + assertAuditMessage(hit, "transport", "authentication_success"); + assertEquals("transport", sourceMap.get("origin_type")); + if (runAs) { + assertThat(sourceMap.get("principal"), is("running as")); + assertThat(sourceMap.get("run_by_principal"), is("_username")); + } else { + assertEquals("_username", sourceMap.get("principal")); + } + assertEquals("_action", sourceMap.get("action")); + assertEquals("_realm", sourceMap.get("realm")); + assertEquals(sourceMap.get("request"), message.getClass().getSimpleName()); + } + private void assertAuditMessage(SearchHit hit, String layer, String type) { Map sourceMap = hit.sourceAsMap(); assertThat(sourceMap.get("@timestamp"), notNullValue()); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java index 8c277524588..2535710d674 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java @@ -35,8 +35,10 @@ import org.junit.Before; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -601,6 +603,82 @@ public class LoggingAuditTrailTests extends ESTestCase { } } + public void testAuthenticationSuccessRest() throws Exception { + RestRequest request = mock(RestRequest.class); + InetAddress address = forge("_hostname", randomBoolean() ? "127.0.0.1" : "::1"); + when(request.getRemoteAddress()).thenReturn(new InetSocketAddress(address, 9200)); + when(request.uri()).thenReturn("_uri"); + Map params = new HashMap<>(); + params.put("foo", "bar"); + when(request.params()).thenReturn(params); + String expectedMessage = prepareRestContent(request); + boolean runAs = randomBoolean(); + User user; + if (runAs) { + user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" })); + } else { + user = new User("_username", new String[] { "r1" }); + } + String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; + 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); + LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + auditTrail.authenticationSuccess(realm, user, request); + if (includeRequestBody) { + assertMsg(logger, Level.INFO, + prefix + "[rest] [authentication_success]\t" + userInfo + ", realm=[_realm], uri=[_uri], params=[" + params + + "], request_body=[" + expectedMessage + "]"); + } else { + assertMsg(logger, Level.INFO, + prefix + "[rest] [authentication_success]\t" + userInfo + ", realm=[_realm], uri=[_uri], params=[" + params + "]"); + } + + // test disabled + CapturingLogger.output(logger.getName(), Level.INFO).clear(); + settings = Settings.builder().put(this.settings).put("xpack.security.audit.logfile.events.exclude", "authentication_success") + .build(); + auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + auditTrail.authenticationSuccess(realm, user, request); + assertEmptyLog(logger); + } + + 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); + LoggingAuditTrail auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + TransportMessage message = randomBoolean() ? new MockMessage(threadContext) : new MockIndicesRequest(threadContext); + String origins = LoggingAuditTrail.originAttributes(message, localNode, threadContext); + boolean runAs = randomBoolean(); + User user; + if (runAs) { + user = new User("_username", new String[] { "r1" }, new User("running as", new String[] { "r2" })); + } else { + user = new User("_username", new String[] { "r1" }); + } + String userInfo = runAs ? "principal=[running as], run_by_principal=[_username]" : "principal=[_username]"; + 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]"); + } else { + assertMsg(logger, Level.INFO, prefix + "[transport] [authentication_success]\t" + origins + ", " + userInfo + + ", realm=[_realm], action=[_action], request=[MockMessage]"); + } + + // test disabled + CapturingLogger.output(logger.getName(), Level.INFO).clear(); + settings = Settings.builder().put(this.settings).put("xpack.security.audit.logfile.events.exclude", "authentication_success").build(); + auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext); + auditTrail.authenticationSuccess(realm, user, "_action", message); + assertEmptyLog(logger); + } + private void assertMsg(Logger logger, Level level, String message) { List output = CapturingLogger.output(logger.getName(), level); assertThat(output.size(), is(1)); diff --git a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java index c536ae36b2b..2036bb5c92f 100644 --- a/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java +++ b/elasticsearch/x-pack/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java @@ -166,7 +166,8 @@ public class AuthenticationServiceTests extends ESTestCase { Authentication result = service.authenticate("_action", message, null); assertThat(result, notNullValue()); assertThat(result.getUser(), is(user)); - verifyZeroInteractions(auditTrail); + verify(auditTrail).authenticationSuccess(secondRealm.name(), user, "_action", message); + verifyNoMoreInteractions(auditTrail); verify(firstRealm, never()).authenticate(token); assertThreadContextContainsAuthentication(result); } @@ -300,6 +301,8 @@ public class AuthenticationServiceTests extends ESTestCase { assertThat(result, notNullValue()); assertThat(result.getUser(), sameInstance(user)); assertThreadContextContainsAuthentication(result); + verify(auditTrail).authenticationSuccess(firstRealm.name(), user, "_action", message); + verifyNoMoreInteractions(auditTrail); } public void testAuthenticateRestSuccess() throws Exception { @@ -311,6 +314,8 @@ public class AuthenticationServiceTests extends ESTestCase { assertThat(result, notNullValue()); assertThat(result.getUser(), sameInstance(user1)); assertThreadContextContainsAuthentication(result); + verify(auditTrail).authenticationSuccess(firstRealm.name(), user1, restRequest); + verifyNoMoreInteractions(auditTrail); } public void testAutheticateTransportContextAndHeader() throws Exception { @@ -472,6 +477,8 @@ public class AuthenticationServiceTests extends ESTestCase { assertThat(result, notNullValue()); assertThat(result.getUser(), sameInstance((Object) anonymousUser)); assertThreadContextContainsAuthentication(result); + verify(auditTrail).authenticationSuccess("__anonymous", new AnonymousUser(settings), request); + verifyNoMoreInteractions(auditTrail); } public void testAnonymousUserTransportNoDefaultUser() throws Exception {