[Backport][API keys] Add full_name and email to API key doc and use them to populate authing User (#61354) (#61403)

The API key document currently doesn't include the user's full_name or email attributes,
and as a result, when those attributes return `null` when hitting `GET`ing  `/_security/_authenticate`,
and in the SAML response from the [IdP Plugin](https://github.com/elastic/elasticsearch/pull/54046).

This changeset adds those fields to the document and extracts them to fill in the User when
authenticating. They're effectively going to be a snapshot of the User from when the key was
created, but this is in line with roles and metadata as well.

Signed-off-by: lloydmeta <lloydmeta@gmail.com>
This commit is contained in:
Lloyd 2020-08-21 18:32:19 +09:00 committed by GitHub
parent e09058df1a
commit cb83e7011c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 5 deletions

View File

@ -188,6 +188,13 @@
"principal" : {
"type": "keyword"
},
"full_name" : {
"type" : "text"
},
"email" : {
"type" : "text",
"analyzer" : "email"
},
"metadata" : {
"type" : "object",
"dynamic" : false

View File

@ -302,6 +302,8 @@ public class ApiKeyService {
.field("version", version.id)
.startObject("creator")
.field("principal", authentication.getUser().principal())
.field("full_name", authentication.getUser().fullName())
.field("email", authentication.getUser().email())
.field("metadata", authentication.getUser().metadata())
.field("realm", authentication.getSourceRealm().getName())
.field("realm_type", authentication.getSourceRealm().getType())
@ -597,8 +599,10 @@ public class ApiKeyService {
ActionListener<AuthenticationResult> listener) {
if (apiKeyDoc.expirationTime == -1 || Instant.ofEpochMilli(apiKeyDoc.expirationTime).isAfter(clock.instant())) {
final String principal = Objects.requireNonNull((String) apiKeyDoc.creator.get("principal"));
final String fullName = (String) apiKeyDoc.creator.get("full_name");
final String email = (String) apiKeyDoc.creator.get("email");
Map<String, Object> metadata = (Map<String, Object>) apiKeyDoc.creator.get("metadata");
final User apiKeyUser = new User(principal, Strings.EMPTY_ARRAY, null, null, metadata, true);
final User apiKeyUser = new User(principal, Strings.EMPTY_ARRAY, fullName, email, metadata, true);
final Map<String, Object> authResultMetadata = new HashMap<>();
authResultMetadata.put(API_KEY_CREATOR_REALM_NAME, apiKeyDoc.creator.get("realm"));
authResultMetadata.put(API_KEY_CREATOR_REALM_TYPE, apiKeyDoc.creator.get("realm_type"));

View File

@ -196,9 +196,26 @@ public class ApiKeyServiceTests extends ESTestCase {
final User user;
if (randomBoolean()) {
user = new User("hulk", new String[] { "superuser" }, new User("authenticated_user", new String[] { "other" }));
user = new User(
new User(
"hulk",
new String[]{"superuser"},
"Bruce Banner",
"hulk@test.com",
org.elasticsearch.common.collect.Map.of(),
true
),
new User("authenticated_user", new String[]{"other"})
);
} else {
user = new User("hulk", new String[] { "superuser" });
user = new User(
"hulk",
new String[]{"superuser"},
"Bruce Banner",
"hulk@test.com",
org.elasticsearch.common.collect.Map.of(),
true
);
}
mockKeyDocument(service, id, key, user);
@ -206,6 +223,8 @@ public class ApiKeyServiceTests extends ESTestCase {
assertThat(auth.getStatus(), is(AuthenticationResult.Status.SUCCESS));
assertThat(auth.getUser(), notNullValue());
assertThat(auth.getUser().principal(), is("hulk"));
assertThat(auth.getUser().fullName(), is("Bruce Banner"));
assertThat(auth.getUser().email(), is("hulk@test.com"));
assertThat(auth.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_NAME), is("realm1"));
assertThat(auth.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_TYPE), is("native"));
assertThat(auth.getMetadata().get(ApiKeyService.API_KEY_ID_KEY), is(id));
@ -377,6 +396,8 @@ public class ApiKeyServiceTests extends ESTestCase {
assertNotNull(result);
assertTrue(result.isAuthenticated());
assertThat(result.getUser().principal(), is("test_user"));
assertThat(result.getUser().fullName(), is("test user"));
assertThat(result.getUser().email(), is("test@user.com"));
assertThat(result.getUser().roles(), is(emptyArray()));
assertThat(result.getUser().metadata(), is(Collections.emptyMap()));
assertThat(result.getMetadata().get(API_KEY_ROLE_DESCRIPTORS_KEY), equalTo(apiKeyDoc.roleDescriptorsBytes));
@ -391,6 +412,8 @@ public class ApiKeyServiceTests extends ESTestCase {
assertNotNull(result);
assertTrue(result.isAuthenticated());
assertThat(result.getUser().principal(), is("test_user"));
assertThat(result.getUser().fullName(), is("test user"));
assertThat(result.getUser().email(), is("test@user.com"));
assertThat(result.getUser().roles(), is(emptyArray()));
assertThat(result.getUser().metadata(), is(Collections.emptyMap()));
assertThat(result.getMetadata().get(API_KEY_ROLE_DESCRIPTORS_KEY), equalTo(apiKeyDoc.roleDescriptorsBytes));
@ -923,6 +946,8 @@ public class ApiKeyServiceTests extends ESTestCase {
sourceMap.put("limited_by_role_descriptors", Collections.singletonMap("limited role", Collections.singletonMap("cluster", "all")));
Map<String, Object> creatorMap = new HashMap<>();
creatorMap.put("principal", "test_user");
creatorMap.put("full_name", "test user");
creatorMap.put("email", "test@user.com");
creatorMap.put("metadata", Collections.emptyMap());
sourceMap.put("creator", creatorMap);
sourceMap.put("api_key_invalidated", false);
@ -954,8 +979,13 @@ public class ApiKeyServiceTests extends ESTestCase {
new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}"),
new BytesArray("{\"limited role\": {\"cluster\": [\"all\"]}}"),
org.elasticsearch.common.collect.Map.of(
"principal", "test_user", "realm", "realm1", "realm_type", "realm_type1", "metadata",
org.elasticsearch.common.collect.Map.of())
"principal", "test_user",
"full_name", "test user",
"email", "test@user.com",
"realm", "realm1",
"realm_type", "realm_type1",
"metadata", org.elasticsearch.common.collect.Map.of()
)
);
}
}

View File

@ -1421,6 +1421,8 @@ public class AuthenticationServiceTests extends ESTestCase {
source.put("version", 0);
Map<String, Object> creatorMap = new HashMap<>();
creatorMap.put("principal", "johndoe");
creatorMap.put("full_name", "john doe");
creatorMap.put("email", "john@doe.com");
creatorMap.put("metadata", Collections.emptyMap());
creatorMap.put("realm", "auth realm");
source.put("creator", creatorMap);
@ -1439,6 +1441,8 @@ public class AuthenticationServiceTests extends ESTestCase {
threadContext.putHeader("Authorization", headerValue);
final Authentication authentication = authenticateBlocking("_action", transportRequest, null);
assertThat(authentication.getUser().principal(), is("johndoe"));
assertThat(authentication.getUser().fullName(), is("john doe"));
assertThat(authentication.getUser().email(), is("john@doe.com"));
assertThat(authentication.getAuthenticationType(), is(AuthenticationType.API_KEY));
}
}