Browse Source

Include authentication type for the authenticate response (#61247)

Add a new "authentication_type" field to the response of "GET _security/_authenticate".
Yang Wang 5 years ago
parent
commit
eecee4438e

+ 13 - 4
client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java

@@ -50,12 +50,13 @@ public final class AuthenticateResponse {
     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");
+    static final ParseField AUTHENTICATION_TYPE = new ParseField("authentication_type");
 
     @SuppressWarnings("unchecked")
     private static final ConstructingObjectParser<AuthenticateResponse, Void> PARSER = new ConstructingObjectParser<>(
             "client_security_authenticate_response", true,
             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], (RealmInfo) a[6], (RealmInfo) a[7]));
+                (String) a[3], (String) a[4]), (Boolean) a[5], (RealmInfo) a[6], (RealmInfo) a[7], (String) a[8]));
     static {
         final ConstructingObjectParser<RealmInfo, Void> realmInfoParser = new ConstructingObjectParser<>("realm_info", true,
             a -> new RealmInfo((String) a[0], (String) a[1]));
@@ -69,20 +70,23 @@ public final class AuthenticateResponse {
         PARSER.declareBoolean(constructorArg(), ENABLED);
         PARSER.declareObject(constructorArg(), realmInfoParser, AUTHENTICATION_REALM);
         PARSER.declareObject(constructorArg(), realmInfoParser, LOOKUP_REALM);
+        PARSER.declareString(constructorArg(), AUTHENTICATION_TYPE);
     }
 
     private final User user;
     private final boolean enabled;
     private final RealmInfo authenticationRealm;
     private final RealmInfo lookupRealm;
+    private final String authenticationType;
 
 
     public AuthenticateResponse(User user, boolean enabled, RealmInfo authenticationRealm,
-                                RealmInfo lookupRealm) {
+                                RealmInfo lookupRealm, String authenticationType) {
         this.user = user;
         this.enabled = enabled;
         this.authenticationRealm = authenticationRealm;
         this.lookupRealm = lookupRealm;
+        this.authenticationType = authenticationType;
     }
 
     /**
@@ -115,6 +119,10 @@ public final class AuthenticateResponse {
         return lookupRealm;
     }
 
+    public String getAuthenticationType() {
+        return authenticationType;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -123,12 +131,13 @@ public final class AuthenticateResponse {
         return enabled == that.enabled &&
             Objects.equals(user, that.user) &&
             Objects.equals(authenticationRealm, that.authenticationRealm) &&
-            Objects.equals(lookupRealm, that.lookupRealm);
+            Objects.equals(lookupRealm, that.lookupRealm) &&
+            Objects.equals(authenticationType, that.authenticationType);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(user, enabled, authenticationRealm, lookupRealm);
+        return Objects.hash(user, enabled, authenticationRealm, lookupRealm, authenticationType);
     }
 
     public static AuthenticateResponse fromXContent(XContentParser parser) throws IOException {

+ 1 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java

@@ -108,6 +108,7 @@ public class SecurityIT extends ESRestHighLevelClientTestCase {
 
         assertThat(authenticateResponse.getUser(), is(putUserRequest.getUser()));
         assertThat(authenticateResponse.enabled(), is(true));
+        assertThat(authenticateResponse.getAuthenticationType(), is("realm"));
 
         // get user
         final GetUsersRequest getUsersRequest =

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

@@ -750,6 +750,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             final String authenticationRealmType = response.getAuthenticationRealm().getType(); // <4>
             final String lookupRealmName = response.getLookupRealm().getName(); // <5>
             final String lookupRealmType = response.getLookupRealm().getType(); // <6>
+            final String authenticationType = response.getAuthenticationType(); // <7>
             //end::authenticate-response
 
             assertThat(user.getUsername(), is("test_user"));
@@ -762,6 +763,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(authenticationRealmType, is("file"));
             assertThat(lookupRealmName, is("default_file"));
             assertThat(lookupRealmType, is("file"));
+            assertThat(authenticationType, is("realm"));
         }
 
         {
@@ -2341,6 +2343,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(authnRealm, is(notNullValue()));
             assertThat(authnRealm.getName(), is("pki1"));
             assertThat(authnRealm.getType(), is("pki"));
+            assertThat(resp.getAuthenticationType(), is("token"));
         }
 
         {
@@ -2384,6 +2387,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(authnRealm, is(notNullValue()));
             assertThat(authnRealm.getName(), is("pki1"));
             assertThat(authnRealm.getType(), is("pki"));
+            assertThat(resp.getAuthenticationType(), is("token"));
         }
     }
 

+ 21 - 11
client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java

@@ -75,10 +75,11 @@ public class AuthenticateResponseTests extends ESTestCase {
         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");
+        final String authenticationType = randomFrom("realm", "api_key", "token", "anonymous", "internal");
         return new AuthenticateResponse(
             new User(username, roles, metadata, fullName, email), enabled,
             new AuthenticateResponse.RealmInfo(authenticationRealmName, authenticationRealmType),
-            new AuthenticateResponse.RealmInfo(lookupRealmName, lookupRealmType));
+            new AuthenticateResponse.RealmInfo(lookupRealmName, lookupRealmType), authenticationType);
     }
 
     private void toXContent(AuthenticateResponse response, XContentBuilder builder) throws IOException {
@@ -103,6 +104,7 @@ public class AuthenticateResponseTests extends ESTestCase {
         builder.field(AuthenticateResponse.REALM_NAME.getPreferredName(), response.getLookupRealm().getName());
         builder.field(AuthenticateResponse.REALM_TYPE.getPreferredName(), response.getLookupRealm().getType());
         builder.endObject();
+        builder.field(AuthenticateResponse.AUTHENTICATION_TYPE.getPreferredName(), response.getAuthenticationType());
         builder.endObject();
     }
 
@@ -111,48 +113,56 @@ public class AuthenticateResponseTests extends ESTestCase {
         final User copyUser = new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
             originalUser.getFullName(), originalUser.getEmail());
         return new AuthenticateResponse(copyUser, response.enabled(), response.getAuthenticationRealm(),
-            response.getLookupRealm());
+            response.getLookupRealm(), response.getAuthenticationType());
     }
 
     private AuthenticateResponse mutate(AuthenticateResponse response) {
         final User originalUser = response.getUser();
-        switch (randomIntBetween(1, 8)) {
+        switch (randomIntBetween(1, 9)) {
             case 1:
                 return new AuthenticateResponse(new User(originalUser.getUsername() + "wrong", originalUser.getRoles(),
                     originalUser.getMetadata(), originalUser.getFullName(), originalUser.getEmail()), response.enabled(),
-                    response.getAuthenticationRealm(), response.getLookupRealm());
+                    response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType());
             case 2:
                 final List<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(), response.getAuthenticationRealm(),
-                    response.getLookupRealm());
+                    response.getLookupRealm(), response.getAuthenticationType());
             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(), response.getAuthenticationRealm(),
-                    response.getLookupRealm());
+                    response.getLookupRealm(), response.getAuthenticationType());
             case 4:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
                     originalUser.getFullName() + "wrong", originalUser.getEmail()), response.enabled(),
-                    response.getAuthenticationRealm(), response.getLookupRealm());
+                    response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType());
             case 5:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
                     originalUser.getFullName(), originalUser.getEmail() + "wrong"), response.enabled(),
-                    response.getAuthenticationRealm(), response.getLookupRealm());
+                    response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType());
             case 6:
                 return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
                     originalUser.getFullName(), originalUser.getEmail()), !response.enabled(), response.getAuthenticationRealm(),
-                    response.getLookupRealm());
+                    response.getLookupRealm(), response.getAuthenticationType());
             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)));
+                    new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)),
+                    response.getAuthenticationType());
             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());
+                    new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm(),
+                    response.getAuthenticationType());
+            case 9:
+                return new AuthenticateResponse(new User(originalUser.getUsername(), originalUser.getRoles(), originalUser.getMetadata(),
+                    originalUser.getFullName(), originalUser.getEmail()), response.enabled(), response.getAuthenticationRealm(),
+                    response.getLookupRealm(),
+                    randomValueOtherThan(response.getAuthenticationType(),
+                                         () -> randomFrom("realm", "api_key", "token", "anonymous", "internal")));
         }
         throw new IllegalStateException("Bad random number");
     }

+ 1 - 0
docs/java-rest/high-level/security/authenticate.asciidoc

@@ -44,6 +44,7 @@ see {javadoc-client}/security/user/User.html.
 <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.
+<7> `getAuthenticationType()` retrieves the authentication type of the authenticated user.
 
 [id="{upid}-{api}-async"]
 ==== Asynchronous Execution

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

@@ -42,7 +42,7 @@ The following example output provides information about the "rdeniro" user:
 --------------------------------------------------
 {
   "username": "rdeniro",
-  "roles": [ 
+  "roles": [
     "admin"
   ],
   "full_name": null,
@@ -56,8 +56,9 @@ The following example output provides information about the "rdeniro" user:
   "lookup_realm": {
     "name" : "file",
     "type" : "file"
-  }
+  },
+  "authentication_type": "realm"
 }
 --------------------------------------------------
 // TESTRESPONSE[s/"rdeniro"/"$body.username"/]
-// TESTRESPONSE[s/"admin"/"superuser"/]
+// TESTRESPONSE[s/"admin"/"superuser"/]

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

@@ -34,4 +34,4 @@ public class AuthenticateResponse extends ActionResponse {
         authentication.writeTo(out);
     }
 
-    }
+}

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

@@ -20,6 +20,7 @@ import org.elasticsearch.xpack.core.security.user.User;
 import java.io.IOException;
 import java.util.Base64;
 import java.util.Collections;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 
@@ -176,6 +177,7 @@ public class Authentication implements ToXContentObject {
             builder.field(User.Fields.REALM_TYPE.getPreferredName(), getAuthenticatedBy().getType());
         }
         builder.endObject();
+        builder.field(User.Fields.AUTHENTICATION_TYPE.getPreferredName(), getAuthenticationType().name().toLowerCase(Locale.ROOT));
     }
 
     @Override
@@ -261,4 +263,3 @@ public class Authentication implements ToXContentObject {
         INTERNAL
     }
 }
-

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

@@ -238,6 +238,7 @@ public class User implements ToXContentObject {
         ParseField LOOKUP_REALM = new ParseField("lookup_realm");
         ParseField REALM_TYPE = new ParseField("type");
         ParseField REALM_NAME = new ParseField("name");
+        ParseField AUTHENTICATION_TYPE = new ParseField("authentication_type");
     }
 }
 

+ 1 - 0
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java

@@ -190,6 +190,7 @@ public class ApiKeyIntegTests extends SecurityIntegTestCase {
         AuthenticateResponse authResponse = restClient.security().authenticate(RequestOptions.DEFAULT.toBuilder().addHeader("Authorization",
             "ApiKey " + base64ApiKeyKeyValue).build());
         assertThat(authResponse.getUser().getUsername(), equalTo(SecuritySettingsSource.TEST_SUPERUSER));
+        assertThat(authResponse.getAuthenticationType(), equalTo("api_key"));
 
         // use the first ApiKey for an unauthorized action
         ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () ->

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

@@ -525,8 +525,9 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         assertNotEquals(refreshResponse.getRefreshToken(), createTokenResponse.getRefreshToken());
 
         AuthenticateResponse response = restClient.security().authenticate(superuserOptions);
-        ;
+
         assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.getUser().getUsername());
+        assertEquals("realm", response.getAuthenticationType());
 
         assertAuthenticateWithToken(createTokenResponse.getAccessToken(), SecuritySettingsSource.TEST_USER_NAME);
         assertAuthenticateWithToken(refreshResponse.getAccessToken(), SecuritySettingsSource.TEST_USER_NAME);
@@ -604,6 +605,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
         AuthenticateResponse authResponse = restClient.security().authenticate(RequestOptions.DEFAULT.toBuilder().addHeader("Authorization",
             "Bearer " + accessToken).build());
         assertThat(authResponse.getUser().getUsername(), equalTo(expectedUser));
+        assertThat(authResponse.getAuthenticationType(), equalTo("token"));
     }
 
     private void assertUnauthorizedToken(String accessToken) {

+ 3 - 0
x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java

@@ -160,6 +160,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
                 assertThat(authnRealm, is(notNullValue()));
                 assertThat(authnRealm.getName(), is("pki3"));
                 assertThat(authnRealm.getType(), is("pki"));
+                assertThat(resp.getAuthenticationType(), is("token"));
             }
         }
     }
@@ -205,6 +206,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
             assertThat(authnRealm, is(notNullValue()));
             assertThat(authnRealm.getName(), is("pki3"));
             assertThat(authnRealm.getType(), is("pki"));
+            assertThat(resp.getAuthenticationType(), is("token"));
             // invalidate
             InvalidateTokenRequest invalidateRequest = new InvalidateTokenRequest(token, null, null, null);
             optionsBuilder = RequestOptions.DEFAULT.toBuilder();
@@ -291,6 +293,7 @@ public class PkiAuthDelegationIntegTests extends SecurityIntegTestCase {
             assertThat(authnRealm, is(notNullValue()));
             assertThat(authnRealm.getName(), is("pki3"));
             assertThat(authnRealm.getType(), is("pki"));
+            assertThat(resp.getAuthenticationType(), is("token"));
             // delete role mappings for delegated PKI
             restClient.security().deleteRoleMapping(new DeleteRoleMappingRequest("role_by_delegated_user", RefreshPolicy.IMMEDIATE),
                   testUserOptions);

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

@@ -65,6 +65,7 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase {
         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"));
+        assertThat(objectPath.evaluate("authentication_type").toString(), equalTo("realm"));
         List<String> roles = objectPath.evaluate("roles");
         assertThat(roles.size(), is(1));
         assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE));

+ 1 - 0
x-pack/plugin/src/test/resources/rest-api-spec/test/authenticate/10_basic.yml

@@ -35,3 +35,4 @@ teardown:
   - match: { username: "authenticate_user" }
   - match: { roles.0:  "superuser" }
   - match: { full_name: "Authenticate User" }
+  - match: { authentication_type: "realm" }