Include authentication type for the authenticate response (#61247) (#61411)

Add a new "authentication_type" field to the response of "GET _security/_authenticate".
This commit is contained in:
Yang Wang 2020-08-21 22:59:43 +10:00 committed by GitHub
parent bba4220982
commit cd52233b94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 58 additions and 20 deletions

View File

@ -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 {

View File

@ -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 =

View File

@ -748,6 +748,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"));
@ -760,6 +761,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
assertThat(authenticationRealmType, is("file"));
assertThat(lookupRealmName, is("default_file"));
assertThat(lookupRealmType, is("file"));
assertThat(authenticationType, is("realm"));
}
{
@ -2339,6 +2341,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"));
}
{
@ -2382,6 +2385,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"));
}
}

View File

@ -76,10 +76,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 {
@ -104,6 +105,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();
}
@ -112,48 +114,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 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(), 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");
}

View File

@ -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

View File

@ -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"/]

View File

@ -46,4 +46,4 @@ public class AuthenticateResponse extends ActionResponse {
}
}
}
}

View File

@ -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;
@ -183,6 +184,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
@ -268,4 +270,3 @@ public class Authentication implements ToXContentObject {
INTERNAL
}
}

View File

@ -239,6 +239,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");
}
}

View File

@ -38,6 +38,7 @@ import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenRespons
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.authc.TokenMetadata;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.client.SecurityClient;
@ -666,6 +667,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
client.execute(AuthenticateAction.INSTANCE, request, authFuture);
AuthenticateResponse response = authFuture.actionGet();
assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.authentication().getUser().principal());
assertEquals(Authentication.AuthenticationType.REALM, response.authentication().getAuthenticationType());
authFuture = new PlainActionFuture<>();
request = new AuthenticateRequest();
@ -674,6 +676,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
.execute(AuthenticateAction.INSTANCE, request, authFuture);
response = authFuture.actionGet();
assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.authentication().getUser().principal());
assertEquals(Authentication.AuthenticationType.TOKEN, response.authentication().getAuthenticationType());
authFuture = new PlainActionFuture<>();
request = new AuthenticateRequest();
@ -682,6 +685,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
.execute(AuthenticateAction.INSTANCE, request, authFuture);
response = authFuture.actionGet();
assertEquals(SecuritySettingsSource.TEST_USER_NAME, response.authentication().getUser().principal());
assertEquals(Authentication.AuthenticationType.TOKEN, response.authentication().getAuthenticationType());
}
public void testClientCredentialsGrant() throws Exception {
@ -701,6 +705,7 @@ public class TokenAuthIntegTests extends SecurityIntegTestCase {
.execute(AuthenticateAction.INSTANCE, request, authFuture);
AuthenticateResponse response = authFuture.get();
assertEquals(SecuritySettingsSource.TEST_SUPERUSER, response.authentication().getUser().principal());
assertEquals(Authentication.AuthenticationType.TOKEN, response.authentication().getAuthenticationType());
// invalidate
PlainActionFuture<InvalidateTokenResponse> invalidateResponseFuture = new PlainActionFuture<>();

View File

@ -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);

View File

@ -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));

View File

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