Skip to content

Commit

Permalink
Preserve thread context during authentication. (#34290)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tvernum authored Oct 5, 2018
1 parent 4dacfa9 commit 1bb2a15
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
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;
Expand Down Expand Up @@ -294,9 +295,9 @@ private void consumeToken(AuthenticationToken token) {
}
};
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ public void testRealmLookupThrowingExceptionRest() throws Exception {
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 {
Expand Down Expand Up @@ -1029,12 +1029,22 @@ void assertThreadContextContainsAuthentication(Authentication authentication) th

@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));
Expand Down

0 comments on commit 1bb2a15

Please sign in to comment.