Preserve thread context during authentication. (#34290)

There may be values in the thread context that ought to be preseved
for later use, even if one or more realms perform asynchronous
authentication.

This commit changes the AuthenticationService to wrap the potentially
asynchronous calls in a ContextPreservingActionListener that retains
the original thread context for the authentication.
This commit is contained in:
Tim Vernum 2018-10-05 12:08:21 +10:00 committed by GitHub
parent 4dacfa95d2
commit 1bb2a1502d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 8 deletions

View File

@ -9,6 +9,7 @@ import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ContextPreservingActionListener;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.component.AbstractComponent;
@ -294,9 +295,9 @@ public class AuthenticationService extends AbstractComponent {
}
};
final IteratingActionListener<User, Realm> authenticatingListener =
new IteratingActionListener<>(ActionListener.wrap(
(user) -> consumeUser(user, messages),
(e) -> listener.onFailure(request.exceptionProcessingRequest(e, token))),
new IteratingActionListener<>(ContextPreservingActionListener.wrapPreservingContext(ActionListener.wrap(
(user) -> consumeUser(user, messages),
(e) -> listener.onFailure(request.exceptionProcessingRequest(e, token))), threadContext),
realmAuthenticatingConsumer, realmsList, threadContext);
try {
authenticatingListener.run();

View File

@ -716,7 +716,7 @@ public class AuthenticationServiceTests extends ESTestCase {
when(secondRealm.supports(token)).thenReturn(true);
mockAuthenticate(secondRealm, token, new User("lookup user", new String[]{"user"}));
mockRealmLookupReturnsNull(firstRealm, "run_as");
doThrow(authenticationError("realm doesn't want to " + "lookup"))
doThrow(authenticationError("realm doesn't want to lookup"))
.when(secondRealm).lookupUser(eq("run_as"), any(ActionListener.class));
try {
@ -1029,12 +1029,22 @@ public class AuthenticationServiceTests extends ESTestCase {
@SuppressWarnings("unchecked")
private void mockAuthenticate(Realm realm, AuthenticationToken token, User user) {
doAnswer((i) -> {
final boolean separateThread = randomBoolean();
doAnswer(i -> {
ActionListener<AuthenticationResult> listener = (ActionListener<AuthenticationResult>) i.getArguments()[1];
if (user == null) {
listener.onResponse(AuthenticationResult.notHandled());
Runnable run = () -> {
if (user == null) {
listener.onResponse(AuthenticationResult.notHandled());
} else {
listener.onResponse(AuthenticationResult.success(user));
}
};
if (separateThread) {
final Thread thread = new Thread(run);
thread.start();
thread.join();
} else {
listener.onResponse(AuthenticationResult.success(user));
run.run();
}
return null;
}).when(realm).authenticate(eq(token), any(ActionListener.class));