diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java index 9bf770d89dbb3..351f793e8969f 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/security/AuthenticateResponse.java +++ b/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 PARSER = new ConstructingObjectParser<>( "client_security_authenticate_response", true, a -> new AuthenticateResponse(new User((String) a[0], ((List) a[1]), (Map) 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 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 RealmInfo getLookupRealm() { return lookupRealm; } + public String getAuthenticationType() { + return authenticationType; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -123,12 +131,13 @@ public boolean equals(Object o) { 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 { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java index a2dc1963d08a7..44a840e7b1bf4 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityIT.java @@ -108,6 +108,7 @@ public void testAuthenticate() throws Exception { assertThat(authenticateResponse.getUser(), is(putUserRequest.getUser())); assertThat(authenticateResponse.enabled(), is(true)); + assertThat(authenticateResponse.getAuthenticationType(), is("realm")); // get user final GetUsersRequest getUsersRequest = diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java index 5c1f454413c7c..fdda0c05f3d6e 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java @@ -750,6 +750,7 @@ public void testAuthenticate() throws Exception { 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 void testAuthenticate() throws Exception { assertThat(authenticationRealmType, is("file")); assertThat(lookupRealmName, is("default_file")); assertThat(lookupRealmType, is("file")); + assertThat(authenticationType, is("realm")); } { @@ -2341,6 +2343,7 @@ public void testDelegatePkiAuthentication() throws Exception { assertThat(authnRealm, is(notNullValue())); assertThat(authnRealm.getName(), is("pki1")); assertThat(authnRealm.getType(), is("pki")); + assertThat(resp.getAuthenticationType(), is("token")); } { @@ -2384,6 +2387,7 @@ public void onFailure(Exception e) { assertThat(authnRealm, is(notNullValue())); assertThat(authnRealm.getName(), is("pki1")); assertThat(authnRealm.getType(), is("pki")); + assertThat(resp.getAuthenticationType(), is("token")); } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java index 7ca84283c501c..5b374ee6ef25a 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/AuthenticateResponseTests.java @@ -75,10 +75,11 @@ protected AuthenticateResponse createTestInstance() { 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 @@ private void toXContent(AuthenticateResponse response, XContentBuilder builder) 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 @@ private AuthenticateResponse copy(AuthenticateResponse response) { 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 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 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"); } diff --git a/docs/java-rest/high-level/security/authenticate.asciidoc b/docs/java-rest/high-level/security/authenticate.asciidoc index 8f2a91a9ca5c6..0a2feb31cf968 100644 --- a/docs/java-rest/high-level/security/authenticate.asciidoc +++ b/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 diff --git a/x-pack/docs/en/rest-api/security/authenticate.asciidoc b/x-pack/docs/en/rest-api/security/authenticate.asciidoc index fc12e1dfb0f0e..d07ebb2c60fb6 100644 --- a/x-pack/docs/en/rest-api/security/authenticate.asciidoc +++ b/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"/] \ No newline at end of file +// TESTRESPONSE[s/"admin"/"superuser"/] diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java index 36faef7569794..a74255e6d2022 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java @@ -34,4 +34,4 @@ public void writeTo(StreamOutput out) throws IOException { authentication.writeTo(out); } - } +} diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java index bef92cb9ab3ce..553d721578682 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/Authentication.java @@ -20,6 +20,7 @@ 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 void toXContentFragment(XContentBuilder builder) throws IOException { 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 enum AuthenticationType { INTERNAL } } - diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java index 12cb2f6bc7ce0..710f02344e5d3 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java @@ -238,6 +238,7 @@ public interface Fields { 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"); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java index 9d68a5eee89a5..1929e3feaef1a 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java @@ -190,6 +190,7 @@ public void testCreateApiKey() throws Exception{ 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, () -> diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java index 9047f484fb290..4a1d8e26f675b 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/TokenAuthIntegTests.java @@ -525,8 +525,9 @@ public void testCreateThenRefreshAsDifferentUser() throws IOException { 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 @@ private void assertAuthenticateWithToken(String accessToken, String expectedUser 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) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java index 1792858648c95..7313f0de08083 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/pki/PkiAuthDelegationIntegTests.java @@ -160,6 +160,7 @@ public void testDelegateThenAuthenticate() throws Exception { assertThat(authnRealm, is(notNullValue())); assertThat(authnRealm.getName(), is("pki3")); assertThat(authnRealm.getType(), is("pki")); + assertThat(resp.getAuthenticationType(), is("token")); } } } @@ -205,6 +206,7 @@ public void testTokenInvalidate() throws Exception { 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 void testDelegatePkiWithRoleMapping() throws Exception { 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); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java index 1e7955175f6d2..e2d2e096d207d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java @@ -65,6 +65,7 @@ public void testAuthenticateApi() throws Exception { 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 roles = objectPath.evaluate("roles"); assertThat(roles.size(), is(1)); assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE)); diff --git a/x-pack/plugin/src/test/resources/rest-api-spec/test/authenticate/10_basic.yml b/x-pack/plugin/src/test/resources/rest-api-spec/test/authenticate/10_basic.yml index ea4f2f592c67e..9cc6634c6103e 100644 --- a/x-pack/plugin/src/test/resources/rest-api-spec/test/authenticate/10_basic.yml +++ b/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" }