Skip to content

Commit 8d83688

Browse files
authored
Allow an AuthenticationResult to return metadata (#34382)
PR #34290 made it impossible to use thread-context values to pass authentication metadata out of a realm. The SAML realm used this technique to allow the SamlAuthenticateAction to process the parsed SAML token, and apply them to the access token that was generated. This new method adds metadata to the AuthenticationResult itself, and then the authentication service makes this result available on the thread context. Closes: #34332
1 parent 21c759a commit 8d83688

File tree

4 files changed

+39
-7
lines changed

4 files changed

+39
-7
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationResult.java

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.elasticsearch.common.Nullable;
99
import org.elasticsearch.xpack.core.security.user.User;
1010

11+
import java.util.Collections;
12+
import java.util.Map;
1113
import java.util.Objects;
1214

1315
/**
@@ -21,7 +23,9 @@
2123
* </ol>
2224
*/
2325
public final class AuthenticationResult {
24-
private static final AuthenticationResult NOT_HANDLED = new AuthenticationResult(Status.CONTINUE, null, null, null);
26+
private static final AuthenticationResult NOT_HANDLED = new AuthenticationResult(Status.CONTINUE, null, null, null, null);
27+
28+
public static String THREAD_CONTEXT_KEY = "_xpack_security_auth_result";
2529

2630
public enum Status {
2731
SUCCESS,
@@ -33,12 +37,15 @@ public enum Status {
3337
private final User user;
3438
private final String message;
3539
private final Exception exception;
40+
private final Map<String, Object> metadata;
3641

37-
private AuthenticationResult(Status status, @Nullable User user, @Nullable String message, @Nullable Exception exception) {
42+
private AuthenticationResult(Status status, @Nullable User user, @Nullable String message, @Nullable Exception exception,
43+
@Nullable Map<String, Object> metadata) {
3844
this.status = status;
3945
this.user = user;
4046
this.message = message;
4147
this.exception = exception;
48+
this.metadata = metadata == null ? Collections.emptyMap() : Collections.unmodifiableMap(metadata);
4249
}
4350

4451
public Status getStatus() {
@@ -57,6 +64,10 @@ public Exception getException() {
5764
return exception;
5865
}
5966

67+
public Map<String, Object> getMetadata() {
68+
return metadata;
69+
}
70+
6071
/**
6172
* Creates an {@code AuthenticationResult} that indicates that the supplied {@link User}
6273
* has been successfully authenticated.
@@ -69,7 +80,16 @@ public Exception getException() {
6980
*/
7081
public static AuthenticationResult success(User user) {
7182
Objects.requireNonNull(user);
72-
return new AuthenticationResult(Status.SUCCESS, user, null, null);
83+
return success(user, null);
84+
}
85+
86+
/**
87+
* Creates a successful result, with optional metadata
88+
*
89+
* @see #success(User)
90+
*/
91+
public static AuthenticationResult success(User user, @Nullable Map<String, Object> metadata) {
92+
return new AuthenticationResult(Status.SUCCESS, user, null, null, metadata);
7393
}
7494

7595
/**
@@ -96,7 +116,7 @@ public static AuthenticationResult notHandled() {
96116
*/
97117
public static AuthenticationResult unsuccessful(String message, @Nullable Exception cause) {
98118
Objects.requireNonNull(message);
99-
return new AuthenticationResult(Status.CONTINUE, null, message, cause);
119+
return new AuthenticationResult(Status.CONTINUE, null, message, cause, null);
100120
}
101121

102122
/**
@@ -110,7 +130,7 @@ public static AuthenticationResult unsuccessful(String message, @Nullable Except
110130
* </p>
111131
*/
112132
public static AuthenticationResult terminate(String message, @Nullable Exception cause) {
113-
return new AuthenticationResult(Status.TERMINATE, null, message, cause);
133+
return new AuthenticationResult(Status.TERMINATE, null, message, cause, null);
114134
}
115135

116136
public boolean isAuthenticated() {

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/saml/TransportSamlAuthenticateAction.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateRequest;
2323
import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateResponse;
2424
import org.elasticsearch.xpack.core.security.authc.Authentication;
25+
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
2526
import org.elasticsearch.xpack.security.authc.AuthenticationService;
2627
import org.elasticsearch.xpack.security.authc.TokenService;
2728
import org.elasticsearch.xpack.security.authc.saml.SamlRealm;
@@ -54,7 +55,12 @@ protected void doExecute(Task task, SamlAuthenticateRequest request, ActionListe
5455
Authentication originatingAuthentication = Authentication.getAuthentication(threadContext);
5556
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
5657
authenticationService.authenticate(SamlAuthenticateAction.NAME, request, saml, ActionListener.wrap(authentication -> {
57-
final Map<String, Object> tokenMeta = threadContext.getTransient(SamlRealm.CONTEXT_TOKEN_DATA);
58+
AuthenticationResult result = threadContext.getTransient(AuthenticationResult.THREAD_CONTEXT_KEY);
59+
if (result == null) {
60+
listener.onFailure(new IllegalStateException("Cannot find AuthenticationResult on thread context"));
61+
return;
62+
}
63+
final Map<String, Object> tokenMeta = (Map<String, Object>) result.getMetadata().get(SamlRealm.CONTEXT_TOKEN_DATA);
5864
tokenService.createUserToken(authentication, originatingAuthentication,
5965
ActionListener.wrap(tuple -> {
6066
final String tokenString = tokenService.getUserTokenString(tuple.v1());

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class Authenticator {
140140
private RealmRef authenticatedBy = null;
141141
private RealmRef lookedupBy = null;
142142
private AuthenticationToken authenticationToken = null;
143+
private AuthenticationResult authenticationResult = null;
143144

144145
Authenticator(RestRequest request, ActionListener<Authentication> listener) {
145146
this(new AuditableRestRequest(auditTrail, failureHandler, threadContext, request), null, listener);
@@ -267,6 +268,7 @@ private void consumeToken(AuthenticationToken token) {
267268
if (result.getStatus() == AuthenticationResult.Status.SUCCESS) {
268269
// user was authenticated, populate the authenticated by information
269270
authenticatedBy = new RealmRef(realm.name(), realm.type(), nodeName);
271+
authenticationResult = result;
270272
userListener.onResponse(result.getUser());
271273
} else {
272274
// the user was not authenticated, call this so we can audit the correct event
@@ -360,6 +362,7 @@ private void consumeUser(User user, Map<Realm, Tuple<String, Exception>> message
360362
});
361363
listener.onFailure(request.authenticationFailed(authenticationToken));
362364
} else {
365+
threadContext.putTransient(AuthenticationResult.THREAD_CONTEXT_KEY, authenticationResult);
363366
if (runAsEnabled) {
364367
final String runAsUsername = threadContext.getHeader(AuthenticationServiceField.RUN_AS_USER_HEADER);
365368
if (runAsUsername != null && runAsUsername.isEmpty() == false) {

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/saml/SamlRealm.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,10 @@ private void buildUser(SamlAttributes attributes, ActionListener<AuthenticationR
426426
final Map<String, Object> tokenMetadata = createTokenMetadata(attributes.name(), attributes.session());
427427
ActionListener<AuthenticationResult> wrappedListener = ActionListener.wrap(auth -> {
428428
if (auth.isAuthenticated()) {
429-
config.threadContext().putTransient(CONTEXT_TOKEN_DATA, tokenMetadata);
429+
// Add the SAML token details as metadata on the authentication
430+
Map<String, Object> metadata = new HashMap<>(auth.getMetadata());
431+
metadata.put(CONTEXT_TOKEN_DATA, tokenMetadata);
432+
auth = AuthenticationResult.success(auth.getUser(), metadata);
430433
}
431434
baseListener.onResponse(auth);
432435
}, baseListener::onFailure);

0 commit comments

Comments
 (0)