From 6520fa2b7c3c28cff8e701879465c58fcc3b852a Mon Sep 17 00:00:00 2001 From: Ankit Sethi Date: Tue, 1 Jul 2025 09:45:54 -0500 Subject: [PATCH] Add cloud API keys to auth info report (#130041) * add change and test * improve test --- .../core/security/xcontent/XContentUtils.java | 15 ++++++-- .../authc/AuthenticationTestHelper.java | 38 +++++++++++-------- .../security/xcontent/XContentUtilsTests.java | 17 +++++++++ 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtils.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtils.java index 9656237eb504..13604d199c64 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtils.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtils.java @@ -112,9 +112,7 @@ public class XContentUtils { private static void addSubjectInfo(XContentBuilder builder, Subject subject) throws IOException { switch (subject.getType()) { case USER -> builder.array(User.Fields.ROLES.getPreferredName(), subject.getUser().roles()); - case API_KEY -> { - addApiKeyInfo(builder, subject); - } + case API_KEY -> addApiKeyInfo(builder, subject); case SERVICE_ACCOUNT -> builder.field("service_account", subject.getUser().principal()); case CROSS_CLUSTER_ACCESS -> { builder.startObject("cross_cluster_access"); @@ -129,7 +127,16 @@ public class XContentUtils { builder.endObject(); } case CLOUD_API_KEY -> { - // TODO Add cloud API key information here + builder.startObject("cloud_api_key"); + Map metadata = subject.getUser().metadata(); + builder.field("id", subject.getUser().principal()); + Object name = metadata.get(AuthenticationField.API_KEY_NAME_KEY); + if (name instanceof String) { + builder.field("name", name); + } + builder.field("internal", metadata.get(AuthenticationField.API_KEY_INTERNAL_KEY)); + builder.array(User.Fields.ROLES.getPreferredName(), subject.getUser().roles()); + builder.endObject(); } } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java index 5d07cc97caf3..e9449e784450 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTestHelper.java @@ -92,6 +92,27 @@ public class AuthenticationTestHelper { ); } + public static User randomCloudApiKeyUser() { + return randomCloudApiKeyUser(null); + } + + public static User randomCloudApiKeyUser(String principal) { + final Map metadata = ESTestCase.randomBoolean() + ? null + : Map.ofEntries( + Map.entry(AuthenticationField.API_KEY_NAME_KEY, ESTestCase.randomAlphanumericOfLength(64)), + Map.entry(AuthenticationField.API_KEY_INTERNAL_KEY, ESTestCase.randomBoolean()) + ); + return new User( + principal == null ? ESTestCase.randomAlphanumericOfLength(64) : principal, + ESTestCase.randomArray(1, 3, String[]::new, () -> "role_" + ESTestCase.randomAlphaOfLengthBetween(3, 8)), + null, + null, + metadata, + true + ); + } + public static InternalUser randomInternalUser() { return ESTestCase.randomFrom(InternalUsers.get()); } @@ -260,27 +281,14 @@ public class AuthenticationTestHelper { if (apiKeyId == null) { apiKeyId = user != null ? user.principal() : ESTestCase.randomAlphanumericOfLength(64); } - final Map metadata = ESTestCase.randomBoolean() - ? null - : Map.ofEntries( - Map.entry(AuthenticationField.API_KEY_NAME_KEY, ESTestCase.randomAlphanumericOfLength(64)), - Map.entry(AuthenticationField.API_KEY_INTERNAL_KEY, ESTestCase.randomBoolean()) - ); if (user == null) { - user = new User( - apiKeyId, - ESTestCase.randomArray(1, 3, String[]::new, () -> "role_" + ESTestCase.randomAlphaOfLengthBetween(3, 8)), - null, - null, - metadata, - true - ); + user = randomCloudApiKeyUser(apiKeyId); } assert user.principal().equals(apiKeyId) : "user principal must match cloud API key ID"; return Authentication.newCloudApiKeyAuthentication( - AuthenticationResult.success(user, metadata), + AuthenticationResult.success(user, user.metadata()), "node_" + ESTestCase.randomAlphaOfLengthBetween(3, 8) ); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtilsTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtilsTests.java index 44278f3b6aba..0d38abcdb709 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtilsTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/xcontent/XContentUtilsTests.java @@ -24,8 +24,10 @@ import java.util.Map; import java.util.stream.Collectors; import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ID_KEY; +import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_INTERNAL_KEY; import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_NAME_KEY; import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.CROSS_CLUSTER_ACCESS_AUTHENTICATION_KEY; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class XContentUtilsTests extends ESTestCase { @@ -62,6 +64,21 @@ public class XContentUtilsTests extends ESTestCase { assertThat(json, equalTo("{\"authorization\":{\"api_key\":{\"id\":\"" + apiKeyId + "\",\"name\":\"" + apiKeyName + "\"}}}")); } + public void testAddAuthorizationInfoWithCloudApiKey() throws IOException { + User user = AuthenticationTestHelper.randomCloudApiKeyUser(); + Authentication authentication = AuthenticationTestHelper.randomCloudApiKeyAuthentication(user); + String json = generateJson(Map.of(AuthenticationField.AUTHENTICATION_KEY, authentication.encode())); + assertThat(json, containsString("{\"authorization\":{\"cloud_api_key\":{\"id\":\"" + user.principal())); + assertThat(json, containsString("\"internal\":" + user.metadata().getOrDefault(API_KEY_INTERNAL_KEY, null))); + if (user.metadata().containsKey(API_KEY_NAME_KEY)) { + assertThat(json, containsString("\"name\":\"" + user.metadata().getOrDefault(API_KEY_NAME_KEY, null) + "\"")); + } + for (String role : user.roles()) { + assertThat(json, containsString(role)); + } + + } + public void testAddAuthorizationInfoWithServiceAccount() throws IOException { String account = "elastic/" + randomFrom("kibana", "fleet-server"); User user = new User(account);