Skip to content

Commit

Permalink
Service Accounts - auditing tests for existing fields (elastic#71668)
Browse files Browse the repository at this point in the history
This PR ensure existing auditing tests cover service account authentication. 
It makes sure compatible existing fields also work for service accounts.
  • Loading branch information
ywangd authored Apr 14, 2021
1 parent 2318be0 commit 7a50ccd
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivileges;
import org.elasticsearch.xpack.core.security.support.ValidationTests;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.AsyncSearchUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
Expand Down Expand Up @@ -2037,7 +2038,6 @@ public void testRequestsWithoutIndices() throws Exception {
.put("xpack.security.audit.logfile.events.include", "_all")
.build();
auditTrail = new LoggingAuditTrail(settings, clusterService, logger, threadContext);
final User user = new User("_username", new String[] { "r1" });
final AuthorizationInfo authorizationInfo =
() -> Collections.singletonMap(PRINCIPAL_ROLES_FIELD_NAME, new String[] { randomAlphaOfLengthBetween(1, 6) });
final String realm = randomAlphaOfLengthBetween(1, 6);
Expand Down Expand Up @@ -2205,17 +2205,35 @@ private Authentication createAuthentication() {
final RealmRef lookedUpBy;
final RealmRef authBy;
final User user;
if (randomBoolean()) {
user = new User(randomAlphaOfLength(4), new String[] { "r1" }, new User("authenticated_username", new String[] { "r2" }));
lookedUpBy = new RealmRef(randomAlphaOfLength(4), "lookup", "by");
authBy = new RealmRef("authRealm", "auth", "foo");
} else {
user = new User(randomAlphaOfLength(4), new String[] { "r1" });
lookedUpBy = null;
authBy = new RealmRef(randomAlphaOfLength(4), "auth", "by");
}
return new Authentication(user, authBy, lookedUpBy, Version.CURRENT, randomFrom(AuthenticationType.REALM,
AuthenticationType.TOKEN, AuthenticationType.INTERNAL, AuthenticationType.ANONYMOUS), Collections.emptyMap());
final AuthenticationType authenticationType;
final Map<String, Object> authMetadata;
switch (randomIntBetween(0, 2)) {
case 0:
user = new User(randomAlphaOfLength(4), new String[] { "r1" }, new User("authenticated_username", "r2"));
lookedUpBy = new RealmRef(randomAlphaOfLength(4), "lookup", "by");
authBy = new RealmRef("authRealm", "auth", "foo");
authenticationType= randomFrom(AuthenticationType.REALM, AuthenticationType.TOKEN,
AuthenticationType.INTERNAL, AuthenticationType.ANONYMOUS);
authMetadata = Map.of();
break;
case 1:
user = new User(randomAlphaOfLength(4), "r1");
lookedUpBy = null;
authBy = new RealmRef(randomAlphaOfLength(4), "auth", "by");
authenticationType= randomFrom(AuthenticationType.REALM, AuthenticationType.TOKEN,
AuthenticationType.INTERNAL, AuthenticationType.ANONYMOUS);
authMetadata = Map.of();
break;
default: // service account
final String principal = randomAlphaOfLengthBetween(3, 8) + "/" + randomAlphaOfLengthBetween(3, 8);
user = new User(principal, Strings.EMPTY_ARRAY, "Service account - " + principal, null,
Map.of("_elastic_service_account", true), true);
lookedUpBy = null;
authBy = new RealmRef("service_account", "service_account", randomAlphaOfLengthBetween(3, 8));
authenticationType = AuthenticationType.TOKEN;
authMetadata = Map.of("_token_name", ValidationTests.randomTokenName());
}
return new Authentication(user, authBy, lookedUpBy, Version.CURRENT, authenticationType, authMetadata);
}

private ClusterSettings mockClusterSettings() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.GetRequest;
Expand Down Expand Up @@ -82,6 +83,7 @@
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.EmptyAuthorizationInfo;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.core.security.support.ValidationTests;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.User;
Expand All @@ -93,6 +95,7 @@
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.service.ServiceAccountService;
import org.elasticsearch.xpack.security.authc.service.ServiceAccountToken;
import org.elasticsearch.xpack.security.operator.OperatorPrivileges;
import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
Expand Down Expand Up @@ -1915,36 +1918,61 @@ public void testCanAuthenticateServiceAccount() throws ExecutionException, Inter
Mockito.reset(serviceAccountService);
final Authentication authentication = new Authentication(
new User("elastic/fleet-server"),
new RealmRef("service_account", "service_account", "foo"), null);
new RealmRef("service_account", "service_account", "foo"), null,
Version.CURRENT, AuthenticationType.TOKEN, Map.of("_token_name", ValidationTests.randomTokenName()));
try (ThreadContext.StoredContext ignored = threadContext.newStoredContext(false)) {
boolean requestIdAlreadyPresent = randomBoolean();
SetOnce<String> reqId = new SetOnce<>();
if (requestIdAlreadyPresent) {
reqId.set(AuditUtil.getOrGenerateRequestId(threadContext));
}
threadContext.putHeader("Authorization", "Bearer AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuMTpyNXdkYmRib1FTZTl2R09Ld2FKR0F3");
doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
final ActionListener<Authentication> listener = (ActionListener<Authentication>) invocationOnMock.getArguments()[2];
listener.onResponse(authentication);
return null;
}).when(serviceAccountService).authenticateToken(any(), any(), any());
final PlainActionFuture<Authentication> future = new PlainActionFuture<>();
service.authenticate("_action", transportRequest, false, future);
assertThat(future.get(), is(authentication));
final Tuple<Authentication, String> result = authenticateBlocking("_action", transportRequest, null);
if (requestIdAlreadyPresent) {
assertThat(expectAuditRequestId(threadContext), is(reqId.get()));
}
assertThat(expectAuditRequestId(threadContext), is(result.v2()));
assertThat(result.v1(), is(authentication));
verify(auditTrail).authenticationSuccess(eq(result.v2()), same(authentication), eq("_action"), same(transportRequest));
verify(operatorPrivilegesService).maybeMarkOperatorUser(eq(result.v1()), eq(threadContext));
verifyNoMoreInteractions(auditTrail);
}
}

public void testServiceAccountFailureWillNotFallthrough() {
public void testServiceAccountFailureWillNotFallthrough() throws IOException {
Mockito.reset(serviceAccountService);
final RuntimeException bailOut = new RuntimeException("bail out");
final ElasticsearchSecurityException bailOut = new ElasticsearchSecurityException("bail out", RestStatus.UNAUTHORIZED);
try (ThreadContext.StoredContext ignored = threadContext.newStoredContext(false)) {
threadContext.putHeader("Authorization", "Bearer AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuMTpyNXdkYmRib1FTZTl2R09Ld2FKR0F3");
boolean requestIdAlreadyPresent = randomBoolean();
SetOnce<String> reqId = new SetOnce<>();
if (requestIdAlreadyPresent) {
reqId.set(AuditUtil.getOrGenerateRequestId(threadContext));
}
final String bearerString = "AAEAAWVsYXN0aWMvZmxlZXQtc2VydmVyL3Rva2VuMTpyNXdkYmRib1FTZTl2R09Ld2FKR0F3";
threadContext.putHeader("Authorization", "Bearer " + bearerString);
doAnswer(invocationOnMock -> {
@SuppressWarnings("unchecked")
final ActionListener<Authentication> listener = (ActionListener<Authentication>) invocationOnMock.getArguments()[2];
listener.onFailure(bailOut);
return null;
}).when(serviceAccountService).authenticateToken(any(), any(), any());
final PlainActionFuture<Authentication> future = new PlainActionFuture<>();
service.authenticate("_action", transportRequest, false, future);
final ExecutionException e = expectThrows(ExecutionException.class, () -> future.get());
assertThat(e.getCause().getCause(), is(bailOut));
final ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class,
() -> authenticateBlocking("_action", transportRequest, null));
if (requestIdAlreadyPresent) {
assertThat(expectAuditRequestId(threadContext), is(reqId.get()));
} else {
reqId.set(expectAuditRequestId(threadContext));
}
assertThat(e, sameInstance(bailOut));
verifyZeroInteractions(operatorPrivilegesService);
final ServiceAccountToken serviceToken = ServiceAccountToken.fromBearerString(new SecureString(bearerString.toCharArray()));
verify(auditTrail).authenticationFailed(eq(reqId.get()), eq(serviceToken), eq("_action"), eq(transportRequest));
}
}

Expand Down

0 comments on commit 7a50ccd

Please sign in to comment.