Browse Source

Add realm information for Authenticate API (#35648)

- Add the authentication realm and lookup realm name and type in the response for the _authenticate API
- The authentication realm is set as the lookup realm too (instead of setting the lookup realm to null or empty ) when no lookup realm is used.
Ioannis Kakavas 6 years ago
parent
commit
580b5baf21
15 changed files with 222 additions and 47 deletions
  1. 71 11
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java
  2. 8 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java
  3. 39 9
      client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java
  4. 11 3
      docs/java-rest/high-level/security/authenticate.asciidoc
  5. 11 3
      x-pack/docs/en/rest-api/security/authenticate.asciidoc
  6. 20 7
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java
  7. 28 1
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java
  8. 4 0
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java
  9. 4 2
      x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/user/TransportAuthenticateAction.java
  10. 1 1
      x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java
  11. 9 3
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportAuthenticateActionTests.java
  12. 4 4
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java
  13. 5 1
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java
  14. 6 1
      x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java
  15. 1 1
      x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRunAsIT.java

+ 71 - 11
client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java

@@ -46,27 +46,43 @@ public final class AuthenticateResponse {
     static final ParseField FULL_NAME = new ParseField("full_name");
     static final ParseField EMAIL = new ParseField("email");
     static final ParseField ENABLED = new ParseField("enabled");
+    static final ParseField AUTHENTICATION_REALM = new ParseField("authentication_realm");
+    static final ParseField LOOKUP_REALM = new ParseField("lookup_realm");
+    static final ParseField REALM_NAME = new ParseField("name");
+    static final ParseField REALM_TYPE = new ParseField("type");
 
     @SuppressWarnings("unchecked")
     private static final ConstructingObjectParser<AuthenticateResponse, Void> PARSER = new ConstructingObjectParser<>(
             "client_security_authenticate_response",
             a -> new AuthenticateResponse(new User((String) a[0], ((List<String>) a[1]), (Map<String, Object>) a[2],
-                    (String) a[3], (String) a[4]), (Boolean) a[5]));
+                (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7]));
     static {
+        final ConstructingObjectParser<RealmInfo, Void> realmInfoParser = new ConstructingObjectParser<>("realm_info",
+            a -> new RealmInfo((String) a[0], (String) a[1]));
+        realmInfoParser.declareString(constructorArg(), REALM_NAME);
+        realmInfoParser.declareString(constructorArg(), REALM_TYPE);
         PARSER.declareString(constructorArg(), USERNAME);
         PARSER.declareStringArray(constructorArg(), ROLES);
         PARSER.<Map<String, Object>>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA);
         PARSER.declareStringOrNull(optionalConstructorArg(), FULL_NAME);
         PARSER.declareStringOrNull(optionalConstructorArg(), EMAIL);
         PARSER.declareBoolean(constructorArg(), ENABLED);
+        PARSER.declareObject(constructorArg(), realmInfoParser, AUTHENTICATION_REALM);
+        PARSER.declareObject(constructorArg(), realmInfoParser, LOOKUP_REALM);
     }
 
     private final User user;
     private final boolean enabled;
+    private final RealmInfo authenticationRealm;
+    private final RealmInfo lookupRealm;
 
-    public AuthenticateResponse(User user, boolean enabled) {
+
+    public AuthenticateResponse(User user, boolean enabled, RealmInfo authenticationRealm,
+                                RealmInfo lookupRealm) {
         this.user = user;
         this.enabled = enabled;
+        this.authenticationRealm = authenticationRealm;
+        this.lookupRealm = lookupRealm;
     }
 
     /**
@@ -85,25 +101,69 @@ public final class AuthenticateResponse {
         return enabled;
     }
 
+    /**
+     * @return the realm that authenticated the user
+     */
+    public RealmInfo getAuthenticationRealm() {
+        return authenticationRealm;
+    }
+
+    /**
+     * @return the realm where the user information was looked up
+     */
+    public RealmInfo getLookupRealm() {
+        return lookupRealm;
+    }
+
     @Override
     public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        final AuthenticateResponse that = (AuthenticateResponse) o;
-        return user.equals(that.user) && enabled == that.enabled;
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        AuthenticateResponse that = (AuthenticateResponse) o;
+        return enabled == that.enabled &&
+            Objects.equals(user, that.user) &&
+            Objects.equals(authenticationRealm, that.authenticationRealm) &&
+            Objects.equals(lookupRealm, that.lookupRealm);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(user, enabled);
+        return Objects.hash(user, enabled, authenticationRealm, lookupRealm);
     }
 
     public static AuthenticateResponse fromXContent(XContentParser parser) throws IOException {
         return PARSER.parse(parser, null);
     }
 
+    public static class RealmInfo {
+        private String name;
+        private String type;
+
+        RealmInfo(String name, String type) {
+            this.name = name;
+            this.type = type;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            RealmInfo realmInfo = (RealmInfo) o;
+            return Objects.equals(name, realmInfo.name) &&
+                Objects.equals(type, realmInfo.type);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name, type);
+        }
+    }
 }

+ 8 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java

@@ -532,6 +532,10 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             //tag::authenticate-response
             User user = response.getUser(); // <1>
             boolean enabled = response.enabled(); // <2>
+            final String authenticationRealmName = response.getAuthenticationRealm().getName(); // <3>
+            final String authenticationRealmType = response.getAuthenticationRealm().getType(); // <4>
+            final String lookupRealmName = response.getLookupRealm().getName(); // <5>
+            final String lookupRealmType = response.getLookupRealm().getType(); // <6>
             //end::authenticate-response
 
             assertThat(user.getUsername(), is("test_user"));
@@ -540,6 +544,10 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(user.getEmail(), nullValue());
             assertThat(user.getMetadata().isEmpty(), is(true));
             assertThat(enabled, is(true));
+            assertThat(authenticationRealmName, is("default_file"));
+            assertThat(authenticationRealmType, is("file"));
+            assertThat(lookupRealmName, is("default_file"));
+            assertThat(lookupRealmType, is("file"));
         }
 
         {

+ 39 - 9
client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java

@@ -70,7 +70,14 @@ public class AuthenticateResponseTests extends ESTestCase {
         final String fullName = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 4));
         final String email = randomFrom(random(), null, randomAlphaOfLengthBetween(0, 4));
         final boolean enabled = randomBoolean();
-        return new AuthenticateResponse(new User(username, roles, metadata, fullName, email), enabled);
+        final String authenticationRealmName = randomAlphaOfLength(5);
+        final String authenticationRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos");
+        final String lookupRealmName = randomAlphaOfLength(5);
+        final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos");
+        return new AuthenticateResponse(
+            new User(username, roles, metadata, fullName, email), enabled,
+            new AuthenticateResponse.RealmInfo(authenticationRealmName, authenticationRealmType),
+            new AuthenticateResponse.RealmInfo(lookupRealmName, lookupRealmType));
     }
 
     private void toXContent(AuthenticateResponse response, XContentBuilder builder) throws IOException {
@@ -87,6 +94,14 @@ public class AuthenticateResponseTests extends ESTestCase {
             builder.field(AuthenticateResponse.EMAIL.getPreferredName(), user.getEmail());
         }
         builder.field(AuthenticateResponse.ENABLED.getPreferredName(), enabled);
+        builder.startObject(AuthenticateResponse.AUTHENTICATION_REALM.getPreferredName());
+        builder.field(AuthenticateResponse.REALM_NAME.getPreferredName(), response.getAuthenticationRealm().getName());
+        builder.field(AuthenticateResponse.REALM_TYPE.getPreferredName(), response.getAuthenticationRealm().getType());
+        builder.endObject();
+        builder.startObject(AuthenticateResponse.LOOKUP_REALM.getPreferredName());
+        builder.field(AuthenticateResponse.REALM_NAME.getPreferredName(), response.getLookupRealm().getName());
+        builder.field(AuthenticateResponse.REALM_TYPE.getPreferredName(), response.getLookupRealm().getType());
+        builder.endObject();
         builder.endObject();
     }
 
@@ -94,34 +109,49 @@ public class AuthenticateResponseTests extends ESTestCase {
         final User originalUser = response.getUser();
         final User copyUser = new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
                 originalUser.getFullName(), originalUser.getEmail());
-        return new AuthenticateResponse(copyUser, response.enabled());
+        return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(),
+            response.getLookupRealm());
     }
 
     private AuthenticateResponse mutate(AuthenticateResponse response) {
         final User originalUser = response.getUser();
-        switch (randomIntBetween(1, 6)) {
+        switch (randomIntBetween(1, 8)) {
             case 1:
             return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(),
-                    originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled());
+                    originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(),
+                response.getAuthenticationRealm(), response.getLookupRealm());
             case 2:
                 final Collection<String> wrongRoles = new ArrayList<>(originalUser.getRoles());
                 wrongRoles.add(randomAlphaOfLengthBetween(1, 4));
                 return new AuthenticateResponse(new User(originalUser.getUsername(), wrongRoles, originalUser.getMetadata(),
-                        originalUser.getFullName(), originalUser.getEmail()), response.enabled());
+                    originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(),
+                    response.getLookupRealm());
             case 3:
                 final Map<String, Object> wrongMetadata = new HashMap<>(originalUser.getMetadata());
                 wrongMetadata.put("wrong_string", randomAlphaOfLengthBetween(0, 4));
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), wrongMetadata,
-                        originalUser.getFullName(), originalUser.getEmail()), response.enabled());
+                    originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(),
+                    response.getLookupRealm());
             case 4:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
-                        originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled());
+                        originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(),
+                    response.getAuthenticationRealm(), response.getLookupRealm());
             case 5:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
-                        originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled());
+                        originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(),
+                    response.getAuthenticationRealm(), response.getLookupRealm());
             case 6:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
-                        originalUser.getFullName(), originalUser.getEmail()), !response.enabled());
+                    originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), response.getAuthenticationRealm(),
+                    response.getLookupRealm());
+            case 7:
+                return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
+                    originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(),
+                    new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)));
+            case 8:
+                return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
+                    originalUser.getFullName(), originalUser.getEmail()), response.enabled(),
+                    new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm());
         }
         throw new IllegalStateException("Bad random number");
     }

+ 11 - 3
docs/java-rest/high-level/security/authenticate.asciidoc

@@ -24,10 +24,14 @@ This method does not require a request object. The client waits for the
 [id="{upid}-{api}-response"]
 ==== Response
 
-The returned +{response}+ contains two fields. Firstly, the `user` field
+The returned +{response}+ contains four fields. The `user` field
 , accessed with `getUser`, contains all the information about this
-authenticated user. The other field, `enabled`, tells if this user is actually
-usable or has been temporalily deactivated.
+authenticated user. The field `enabled`, tells if this user is actually
+usable or has been temporarily deactivated. The field `authentication_realm`,
+accessed with `getAuthenticationRealm` contains the name and type of the
+Realm that has authenticated the user and the field `lookup_realm`,
+accessed with `getLookupRealm` contains the name and type of the Realm where
+the user information were retrieved from.
 
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
@@ -36,6 +40,10 @@ include-tagged::{doc-tests-file}[{api}-response]
 <1> `getUser` retrieves the `User` instance containing the information,
 see {javadoc-client}/security/user/User.html.
 <2> `enabled` tells if this user is usable or is deactivated.
+<3> `getAuthenticationRealm().getName()` retrieves the name of the realm that authenticated the user.
+<4> `getAuthenticationRealm().getType()` retrieves the type of the realm that authenticated the user.
+<5> `getLookupRealm().getName()` retrieves the name of the realm from where the user information is looked up.
+<6> `getLookupRealm().getType()` retrieves the type of the realm from where the user information is looked up.
 
 [id="{upid}-{api}-async"]
 ==== Asynchronous Execution

+ 11 - 3
x-pack/docs/en/rest-api/security/authenticate.asciidoc

@@ -13,8 +13,8 @@ authenticate a user and retrieve information about the authenticated user.
 
 ==== Description
 
-A successful call returns a JSON structure that shows what roles are assigned
-to the user as well as any assigned metadata.
+A successful call returns a JSON structure that shows user information such as their username, the roles that are
+assigned to the user, any assigned metadata, and information about the realms that authenticated and authorized the user.
 
 If the user cannot be authenticated, this API returns a 401 status code.
 
@@ -41,7 +41,15 @@ The following example output provides information about the "rdeniro" user:
   "full_name": null,
   "email":  null,
   "metadata": { },
-  "enabled": true
+  "enabled": true,
+  "authentication_realm": {
+    "name" : "default_file",
+    "type" : "file"
+  },
+  "lookup_realm": {
+    "name" : "default_file",
+    "type" : "file"
+  }
 }
 --------------------------------------------------
 // TESTRESPONSE[s/"rdeniro"/"$body.username"/]

+ 20 - 7
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java

@@ -5,36 +5,49 @@
  */
 package org.elasticsearch.xpack.core.security.action.user;
 
+import org.elasticsearch.Version;
 import org.elasticsearch.action.ActionResponse;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.xpack.core.security.authc.Authentication;
 import org.elasticsearch.xpack.core.security.user.User;
 
 import java.io.IOException;
 
 public class AuthenticateResponse extends ActionResponse {
 
-    private User user;
+    private Authentication authentication;
 
     public AuthenticateResponse() {}
 
-    public AuthenticateResponse(User user) {
-        this.user = user;
+    public AuthenticateResponse(Authentication authentication){
+        this.authentication = authentication;
     }
 
-    public User user() {
-        return user;
+    public Authentication authentication() {
+        return authentication;
     }
 
     @Override
     public void writeTo(StreamOutput out) throws IOException {
         super.writeTo(out);
-        User.writeTo(user, out);
+        if (out.getVersion().before(Version.V_7_0_0)) {
+            User.writeTo(authentication.getUser(), out);
+        } else {
+            authentication.writeTo(out);
+        }
     }
 
     @Override
     public void readFrom(StreamInput in) throws IOException {
         super.readFrom(in);
-        user = User.readFrom(in);
+        if (in.getVersion().before(Version.V_7_0_0)) {
+            final User user = User.readFrom(in);
+            final Authentication.RealmRef unknownRealm = new Authentication.RealmRef("__unknown", "__unknown", "__unknown");
+            authentication = new Authentication(user, unknownRealm, unknownRealm);
+        } else {
+            authentication = new Authentication(in);
+        }
     }
+
 }

+ 28 - 1
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java

@@ -11,6 +11,8 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
 import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.util.concurrent.ThreadContext;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.xpack.core.security.user.InternalUserSerializationHelper;
 import org.elasticsearch.xpack.core.security.user.User;
 
@@ -20,7 +22,7 @@ import java.util.Objects;
 
 // TODO(hub-cap) Clean this up after moving User over - This class can re-inherit its field AUTHENTICATION_KEY in AuthenticationField.
 // That interface can be removed
-public class Authentication {
+public class Authentication implements ToXContentObject {
 
     private final User user;
     private final RealmRef authenticatedBy;
@@ -163,6 +165,31 @@ public class Authentication {
         return result;
     }
 
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field(User.Fields.USERNAME.getPreferredName(), user.principal());
+        builder.array(User.Fields.ROLES.getPreferredName(), user.roles());
+        builder.field(User.Fields.FULL_NAME.getPreferredName(), user.fullName());
+        builder.field(User.Fields.EMAIL.getPreferredName(), user.email());
+        builder.field(User.Fields.METADATA.getPreferredName(), user.metadata());
+        builder.field(User.Fields.ENABLED.getPreferredName(), user.enabled());
+        builder.startObject(User.Fields.AUTHENTICATION_REALM.getPreferredName());
+        builder.field(User.Fields.REALM_NAME.getPreferredName(), getAuthenticatedBy().getName());
+        builder.field(User.Fields.REALM_TYPE.getPreferredName(), getAuthenticatedBy().getType());
+        builder.endObject();
+        builder.startObject(User.Fields.LOOKUP_REALM.getPreferredName());
+        if (getLookedUpBy() != null) {
+            builder.field(User.Fields.REALM_NAME.getPreferredName(), getLookedUpBy().getName());
+            builder.field(User.Fields.REALM_TYPE.getPreferredName(), getLookedUpBy().getType());
+        } else {
+            builder.field(User.Fields.REALM_NAME.getPreferredName(), getAuthenticatedBy().getName());
+            builder.field(User.Fields.REALM_TYPE.getPreferredName(), getAuthenticatedBy().getType());
+        }
+        builder.endObject();
+        return builder.endObject();
+    }
+
     public static class RealmRef {
 
         private final String nodeName;

+ 4 - 0
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java

@@ -230,6 +230,10 @@ public class User implements ToXContentObject {
         ParseField METADATA = new ParseField("metadata");
         ParseField ENABLED = new ParseField("enabled");
         ParseField TYPE = new ParseField("type");
+        ParseField AUTHENTICATION_REALM = new ParseField("authentication_realm");
+        ParseField LOOKUP_REALM = new ParseField("lookup_realm");
+        ParseField REALM_TYPE = new ParseField("type");
+        ParseField REALM_NAME = new ParseField("name");
     }
 }
 

+ 4 - 2
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/action/user/TransportAuthenticateAction.java

@@ -16,6 +16,7 @@ import org.elasticsearch.xpack.core.security.SecurityContext;
 import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction;
 import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
 import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
+import org.elasticsearch.xpack.core.security.authc.Authentication;
 import org.elasticsearch.xpack.core.security.user.SystemUser;
 import org.elasticsearch.xpack.core.security.user.User;
 import org.elasticsearch.xpack.core.security.user.XPackUser;
@@ -34,7 +35,8 @@ public class TransportAuthenticateAction extends HandledTransportAction<Authenti
 
     @Override
     protected void doExecute(Task task, AuthenticateRequest request, ActionListener<AuthenticateResponse> listener) {
-        final User runAsUser = securityContext.getUser();
+        final Authentication authentication = securityContext.getAuthentication();
+        final User runAsUser = authentication == null ? null : authentication.getUser();
         final User authUser = runAsUser == null ? null : runAsUser.authenticatedUser();
         if (authUser == null) {
             listener.onFailure(new ElasticsearchSecurityException("did not find an authenticated user"));
@@ -43,7 +45,7 @@ public class TransportAuthenticateAction extends HandledTransportAction<Authenti
         } else if (SystemUser.is(runAsUser) || XPackUser.is(runAsUser)) {
             listener.onFailure(new IllegalArgumentException("user [" + runAsUser.principal() + "] is internal"));
         } else {
-            listener.onResponse(new AuthenticateResponse(runAsUser));
+            listener.onResponse(new AuthenticateResponse(authentication));
         }
     }
 }

+ 1 - 1
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateAction.java

@@ -54,7 +54,7 @@ public class RestAuthenticateAction extends SecurityBaseRestHandler {
                 new RestBuilderListener<AuthenticateResponse>(channel) {
             @Override
             public RestResponse buildResponse(AuthenticateResponse authenticateResponse, XContentBuilder builder) throws Exception {
-                authenticateResponse.user().toXContent(builder, ToXContent.EMPTY_PARAMS);
+                authenticateResponse.authentication().toXContent(builder, ToXContent.EMPTY_PARAMS);
                 return new BytesRestResponse(RestStatus.OK, builder);
             }
         });

+ 9 - 3
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/action/user/TransportAuthenticateActionTests.java

@@ -16,6 +16,7 @@ import org.elasticsearch.transport.TransportService;
 import org.elasticsearch.xpack.core.security.SecurityContext;
 import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
 import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
+import org.elasticsearch.xpack.core.security.authc.Authentication;
 import org.elasticsearch.xpack.core.security.user.ElasticUser;
 import org.elasticsearch.xpack.core.security.user.KibanaUser;
 import org.elasticsearch.xpack.core.security.user.SystemUser;
@@ -37,7 +38,9 @@ public class TransportAuthenticateActionTests extends ESTestCase {
 
     public void testInternalUser() {
         SecurityContext securityContext = mock(SecurityContext.class);
-        when(securityContext.getUser()).thenReturn(randomFrom(SystemUser.INSTANCE, XPackUser.INSTANCE));
+        final Authentication authentication = new Authentication(randomFrom(SystemUser.INSTANCE, XPackUser.INSTANCE),
+            new Authentication.RealmRef("native", "default_native", "node1"), null);
+        when(securityContext.getAuthentication()).thenReturn(authentication);
         TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
             TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
         TransportAuthenticateAction action = new TransportAuthenticateAction(transportService,
@@ -88,9 +91,12 @@ public class TransportAuthenticateActionTests extends ESTestCase {
         assertThat(throwableRef.get().getMessage(), containsString("did not find an authenticated user"));
     }
 
-    public void testValidUser() {
+    public void testValidAuthentication(){
         final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
+        final Authentication authentication = new Authentication(user, new Authentication.RealmRef("native_realm", "native", "node1"),
+            null);
         SecurityContext securityContext = mock(SecurityContext.class);
+        when(securityContext.getAuthentication()).thenReturn(authentication);
         when(securityContext.getUser()).thenReturn(user);
         TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
             TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
@@ -112,7 +118,7 @@ public class TransportAuthenticateActionTests extends ESTestCase {
         });
 
         assertThat(responseRef.get(), notNullValue());
-        assertThat(responseRef.get().user(), sameInstance(user));
+        assertThat(responseRef.get().authentication(), sameInstance(authentication));
         assertThat(throwableRef.get(), nullValue());
     }
 }

+ 4 - 4
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java

@@ -322,7 +322,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         request.username(SecuritySettingsSource.TEST_SUPERUSER);
         client.execute(AuthenticateAction.INSTANCE, request, authFuture);
         AuthenticateResponse response = authFuture.actionGet();
-        assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.user().principal());
+        assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.authentication().getUser().principal());
 
         authFuture = new PlainActionFuture<>();
         request = new AuthenticateRequest();
@@ -330,7 +330,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         client.filterWithHeader(Collections.singletonMap("Authorization", "Bearer " + createTokenResponse.getTokenString()))
                 .execute(AuthenticateAction.INSTANCE, request, authFuture);
         response = authFuture.actionGet();
-        assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.user().principal());
+        assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.authentication().getUser().principal());
 
         authFuture = new PlainActionFuture<>();
         request = new AuthenticateRequest();
@@ -338,7 +338,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         client.filterWithHeader(Collections.singletonMap("Authorization", "Bearer " + refreshResponse.getTokenString()))
                 .execute(AuthenticateAction.INSTANCE, request, authFuture);
         response = authFuture.actionGet();
-        assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.user().principal());
+        assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.authentication().getUser().principal());
     }
 
     public void testClientCredentialsGrant() throws Exception {
@@ -357,7 +357,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         client.filterWithHeader(Collections.singletonMap("Authorization", "Bearer " + createTokenResponse.getTokenString()))
             .execute(AuthenticateAction.INSTANCE, request, authFuture);
         AuthenticateResponse response = authFuture.get();
-        assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.user().principal());
+        assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.authentication().getUser().principal());
 
         // invalidate
         PlainActionFuture<InvalidateTokenResponse> invalidateResponseFuture = new PlainActionFuture<>();

+ 5 - 1
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java

@@ -64,6 +64,7 @@ import static org.elasticsearch.xpack.security.support.SecurityIndexManager.SECU
 import static org.elasticsearch.xpack.security.support.SecurityIndexManager.INTERNAL_SECURITY_INDEX;
 import static org.hamcrest.Matchers.arrayContaining;
 import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.mockito.Mockito.mock;
@@ -576,7 +577,10 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
                                 basicAuthHeaderValue(username, getReservedPassword())))
                 .execute(AuthenticateAction.INSTANCE, new AuthenticateRequest(username))
                 .get();
-        assertThat(authenticateResponse.user().principal(), is(username));
+        assertThat(authenticateResponse.authentication().getUser().principal(), is(username));
+        assertThat(authenticateResponse.authentication().getAuthenticatedBy().getName(), equalTo("reserved"));
+        assertThat(authenticateResponse.authentication().getAuthenticatedBy().getType(), equalTo("reserved"));
+        assertNull(authenticateResponse.authentication().getLookedUpBy());
     }
 
     public void testOperationsOnReservedRoles() throws Exception {

+ 6 - 1
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java

@@ -58,8 +58,13 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase {
         options.addHeader("Authorization", basicAuthHeaderValue(SecuritySettingsSource.TEST_USER_NAME,
                 new SecureString(SecuritySettingsSourceField.TEST_PASSWORD.toCharArray())));
         request.setOptions(options);
-        ObjectPath objectPath = ObjectPath.createFromResponse(getRestClient().performRequest(request));
+        Response a = getRestClient().performRequest(request);
+        ObjectPath objectPath = ObjectPath.createFromResponse(a);
         assertThat(objectPath.evaluate("username").toString(), equalTo(SecuritySettingsSource.TEST_USER_NAME));
+        assertThat(objectPath.evaluate("authentication_realm.name").toString(), equalTo("file"));
+        assertThat(objectPath.evaluate("authentication_realm.type").toString(), equalTo("file"));
+        assertThat(objectPath.evaluate("lookup_realm.name").toString(), equalTo("file"));
+        assertThat(objectPath.evaluate("lookup_realm.type").toString(), equalTo("file"));
         List<String> roles = objectPath.evaluate("roles");
         assertThat(roles.size(), is(1));
         assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE));

+ 1 - 1
x-pack/qa/third-party/active-directory/src/test/java/org/elasticsearch/xpack/security/authc/ldap/ActiveDirectoryRunAsIT.java

@@ -65,7 +65,7 @@ public class ActiveDirectoryRunAsIT extends AbstractAdLdapRealmTestCase {
         final AuthenticateRequest request = new AuthenticateRequest(avenger);
         final ActionFuture<AuthenticateResponse> future = runAsClient(avenger).execute(AuthenticateAction.INSTANCE, request);
         final AuthenticateResponse response = future.get(30, TimeUnit.SECONDS);
-        assertThat(response.user().principal(), Matchers.equalTo(avenger));
+        assertThat(response.authentication().getUser().principal(), Matchers.equalTo(avenger));
     }
 
     protected Client runAsClient(String user) {