Skip to content

Commit 2a22ce7

Browse files
committed
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 c7854e4 commit 2a22ce7

File tree

4 files changed

+39
-8
lines changed

4 files changed

+39
-8
lines changed

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

+25-5
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

+7-1
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(SamlAuthenticateRequest request,
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

+3
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class Authenticator {
138138
private RealmRef authenticatedBy = null;
139139
private RealmRef lookedupBy = null;
140140
private AuthenticationToken authenticationToken = null;
141+
private AuthenticationResult authenticationResult = null;
141142

142143
Authenticator(RestRequest request, ActionListener<Authentication> listener) {
143144
this(new AuditableRestRequest(auditTrail, failureHandler, threadContext, request), null, listener);
@@ -265,6 +266,7 @@ private void consumeToken(AuthenticationToken token) {
265266
if (result.getStatus() == AuthenticationResult.Status.SUCCESS) {
266267
// user was authenticated, populate the authenticated by information
267268
authenticatedBy = new RealmRef(realm.name(), realm.type(), nodeName);
269+
authenticationResult = result;
268270
userListener.onResponse(result.getUser());
269271
} else {
270272
// the user was not authenticated, call this so we can audit the correct event
@@ -358,6 +360,7 @@ private void consumeUser(User user, Map<Realm, Tuple<String, Exception>> message
358360
});
359361
listener.onFailure(request.authenticationFailed(authenticationToken));
360362
} else {
363+
threadContext.putTransient(AuthenticationResult.THREAD_CONTEXT_KEY, authenticationResult);
361364
if (runAsEnabled) {
362365
final String runAsUsername = threadContext.getHeader(AuthenticationServiceField.RUN_AS_USER_HEADER);
363366
if (runAsUsername != null && runAsUsername.isEmpty() == false) {

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,10 @@ private void buildUser(SamlAttributes attributes, ActionListener<AuthenticationR
433433
UserRoleMapper.UserData userData = new UserRoleMapper.UserData(principal, dn, groups, userMeta, config);
434434
roleMapper.resolveRoles(userData, ActionListener.wrap(roles -> {
435435
final User user = new User(principal, roles.toArray(new String[roles.size()]), name, mail, userMeta, true);
436-
config.threadContext().putTransient(CONTEXT_TOKEN_DATA, tokenMetadata);
437-
listener.onResponse(AuthenticationResult.success(user));
436+
// Add the SAML token details as metadata on the authentication
437+
Map<String, Object> authMetadata = new HashMap<>();
438+
authMetadata.put(CONTEXT_TOKEN_DATA, tokenMetadata);
439+
listener.onResponse(AuthenticationResult.success(user, authMetadata));
438440
}, listener::onFailure));
439441
}
440442

0 commit comments

Comments
 (0)