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 e9eeb4454d05f..2aed21588aa7c 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 @@ -44,6 +44,9 @@ public final class AuthenticateResponse implements ToXContentObject { static final ParseField REALM_TYPE = new ParseField("type"); static final ParseField AUTHENTICATION_TYPE = new ParseField("authentication_type"); static final ParseField TOKEN = new ParseField("token"); + static final ParseField API_KEY_INFO = new ParseField("api_key"); // authentication.api_key={"id":"abc123","name":"my-api-key"} + static final ParseField API_KEY_INFO_ID = new ParseField("id"); + static final ParseField API_KEY_INFO_NAME = new ParseField("name"); @SuppressWarnings("unchecked") private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( @@ -55,7 +58,8 @@ public final class AuthenticateResponse implements ToXContentObject { (RealmInfo) a[6], (RealmInfo) a[7], (String) a[8], - (Map) a[9] + (Map) a[9], + (ApiKeyInfo) a[10] ) ); static { @@ -66,6 +70,15 @@ public final class AuthenticateResponse implements ToXContentObject { ); realmInfoParser.declareString(constructorArg(), REALM_NAME); realmInfoParser.declareString(constructorArg(), REALM_TYPE); + + final ConstructingObjectParser apiKeyInfoParser = new ConstructingObjectParser<>( + "api_key", + true, + a -> new ApiKeyInfo((String) a[0], (String) a[1]) + ); + apiKeyInfoParser.declareString(constructorArg(), API_KEY_INFO_ID); + apiKeyInfoParser.declareString(optionalConstructorArg(), API_KEY_INFO_NAME); + PARSER.declareString(constructorArg(), USERNAME); PARSER.declareStringArray(constructorArg(), ROLES); PARSER.>declareObject(constructorArg(), (parser, c) -> parser.map(), METADATA); @@ -76,6 +89,7 @@ public final class AuthenticateResponse implements ToXContentObject { PARSER.declareObject(constructorArg(), realmInfoParser, LOOKUP_REALM); PARSER.declareString(constructorArg(), AUTHENTICATION_TYPE); PARSER.declareObjectOrNull(optionalConstructorArg(), (p, c) -> p.map(), null, TOKEN); + PARSER.declareObjectOrNull(optionalConstructorArg(), apiKeyInfoParser, null, API_KEY_INFO); } private final User user; @@ -85,6 +99,8 @@ public final class AuthenticateResponse implements ToXContentObject { private final String authenticationType; @Nullable private final Map token; + @Nullable + private final ApiKeyInfo apikeyinfo; public AuthenticateResponse( User user, @@ -103,6 +119,18 @@ public AuthenticateResponse( RealmInfo lookupRealm, String authenticationType, @Nullable Map token + ) { + this(user, enabled, authenticationRealm, lookupRealm, authenticationType, null, null); + } + + public AuthenticateResponse( + User user, + boolean enabled, + RealmInfo authenticationRealm, + RealmInfo lookupRealm, + String authenticationType, + @Nullable Map token, + @Nullable ApiKeyInfo apikeyinfo ) { this.user = user; this.enabled = enabled; @@ -110,6 +138,7 @@ public AuthenticateResponse( this.lookupRealm = lookupRealm; this.authenticationType = authenticationType; this.token = token; + this.apikeyinfo = apikeyinfo; } /** @@ -150,6 +179,10 @@ public Map getToken() { return token; } + public ApiKeyInfo getApiKeyInfo() { + return apikeyinfo; + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); @@ -175,6 +208,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws if (token != null) { builder.field(AuthenticateResponse.TOKEN.getPreferredName(), token); } + if (apikeyinfo != null) { + builder.startObject(AuthenticateResponse.API_KEY_INFO.getPreferredName()); + builder.field(AuthenticateResponse.API_KEY_INFO_ID.getPreferredName(), apikeyinfo.getId()); + if (apikeyinfo.getName() != null) { + builder.field(AuthenticateResponse.API_KEY_INFO_NAME.getPreferredName(), apikeyinfo.getName()); + } + builder.endObject(); + } return builder.endObject(); } @@ -188,12 +229,13 @@ public boolean equals(Object o) { && Objects.equals(authenticationRealm, that.authenticationRealm) && Objects.equals(lookupRealm, that.lookupRealm) && Objects.equals(authenticationType, that.authenticationType) - && Objects.equals(token, that.token); + && Objects.equals(token, that.token) + && Objects.equals(apikeyinfo, that.apikeyinfo); } @Override public int hashCode() { - return Objects.hash(user, enabled, authenticationRealm, lookupRealm, authenticationType, token); + return Objects.hash(user, enabled, authenticationRealm, lookupRealm, authenticationType, token, apikeyinfo); } public static AuthenticateResponse fromXContent(XContentParser parser) throws IOException { @@ -230,4 +272,35 @@ public int hashCode() { return Objects.hash(name, type); } } + + public static class ApiKeyInfo { // authentication.api_key={"id":"abc123","name":"my-api-key"} + private String id; + private String name; + + ApiKeyInfo(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final ApiKeyInfo that = (ApiKeyInfo) o; + return Objects.equals(this.id, that.id) && Objects.equals(this.name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + } } 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 9730dab2276e3..0766dcb55a98f 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 @@ -77,13 +77,23 @@ protected AuthenticateResponse createTestInstance() { final String authenticationType = randomFrom("realm", "api_key", "token", "anonymous", "internal"); + final AuthenticateResponse.ApiKeyInfo apiKeyInfo; + if ("api_key".equals(authenticationType)) { + final String apiKeyId = randomAlphaOfLength(16); // mandatory + final String apiKeyName = randomBoolean() ? randomAlphaOfLength(20) : null; // optional + apiKeyInfo = new AuthenticateResponse.ApiKeyInfo(apiKeyId, apiKeyName); + } else { + apiKeyInfo = null; + } + return new AuthenticateResponse( new User(username, roles, metadata, fullName, email), enabled, authenticationRealm, lookupRealm, authenticationType, - tokenInfo + tokenInfo, + apiKeyInfo ); } @@ -106,13 +116,15 @@ private AuthenticateResponse copy(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - Map.copyOf(response.getToken()) + Map.copyOf(response.getToken()), + response.getApiKeyInfo() ); } private AuthenticateResponse mutate(AuthenticateResponse response) { final User originalUser = response.getUser(); - switch (randomIntBetween(1, 10)) { + int randomSwitchCase = randomIntBetween(1, 11); // range is inclusive + switch (randomSwitchCase) { case 1: return new AuthenticateResponse( new User( @@ -126,7 +138,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 2: final List wrongRoles = new ArrayList<>(originalUser.getRoles()); @@ -143,7 +156,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 3: final Map wrongMetadata = new HashMap<>(originalUser.getMetadata()); @@ -160,7 +174,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 4: return new AuthenticateResponse( @@ -174,7 +189,9 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.enabled(), response.getAuthenticationRealm(), response.getLookupRealm(), - response.getAuthenticationType() + response.getAuthenticationType(), + response.getToken(), + response.getApiKeyInfo() ); case 5: return new AuthenticateResponse( @@ -189,7 +206,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 6: return new AuthenticateResponse( @@ -204,7 +222,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 7: return new AuthenticateResponse( @@ -219,7 +238,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationRealm(), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 8: return new AuthenticateResponse( @@ -234,7 +254,8 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), response.getLookupRealm(), response.getAuthenticationType(), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); case 9: return new AuthenticateResponse( @@ -252,9 +273,10 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { response.getAuthenticationType(), () -> randomFrom("realm", "api_key", "token", "anonymous", "internal") ), - response.getToken() + response.getToken(), + response.getApiKeyInfo() ); - default: + case 10: return new AuthenticateResponse( new User( originalUser.getUsername(), @@ -277,9 +299,33 @@ private AuthenticateResponse mutate(AuthenticateResponse response) { randomValueOtherThan(response.getToken().get("type"), () -> randomAlphaOfLengthBetween(3, 8)) ), null + ), + response.getApiKeyInfo() + ); + case 11: + return new AuthenticateResponse( + new User( + originalUser.getUsername(), + originalUser.getRoles(), + originalUser.getMetadata(), + originalUser.getFullName(), + originalUser.getEmail() + ), + response.enabled(), + response.getAuthenticationRealm(), + response.getLookupRealm(), + response.getAuthenticationType(), + response.getToken(), + response.getApiKeyInfo() == null + ? new AuthenticateResponse.ApiKeyInfo( + randomAlphaOfLength(16), // mandatory + randomBoolean() ? randomAlphaOfLength(20) : null // optional ) + : null ); - + default: + fail("Random number " + randomSwitchCase + " did not match any switch cases"); + return null; } } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateTokenResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateTokenResponseTests.java index cbf7212438618..1024bf9f24b24 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateTokenResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateTokenResponseTests.java @@ -34,7 +34,12 @@ public void testFromXContent() throws IOException { true, new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(7)), new AuthenticateResponse.RealmInfo(randomAlphaOfLength(5), randomAlphaOfLength(5)), - "realm" + "realm", + null, + new AuthenticateResponse.ApiKeyInfo( + randomAlphaOfLength(16), // mandatory + randomBoolean() ? randomAlphaOfLength(20) : null // optional + ) ); final XContentType xContentType = randomFrom(XContentType.values()); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java index 5e1dd555c1c39..cee3657d403a3 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java @@ -14,6 +14,7 @@ import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.core.security.authc.Authentication; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.user.User; import java.io.IOException; @@ -82,6 +83,10 @@ protected Authentication createAuthentication() { final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos"); final String nodeName = randomAlphaOfLengthBetween(1, 10); final Authentication.AuthenticationType authenticationType = randomFrom(Authentication.AuthenticationType.values()); + if (Authentication.AuthenticationType.API_KEY.equals(authenticationType)) { + metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10)); + } return new Authentication( new User(username, roles, fullName, email, metadata, true), new Authentication.RealmRef(authenticationRealmName, authenticationRealmType, nodeName), @@ -113,12 +118,22 @@ AuthenticateResponse createServerAuthenticationResponse(Authentication authentic ? authentication.getAuthenticatedBy().getType() : authentication.getLookedUpBy().getType() ); + final AuthenticateResponse.ApiKeyInfo apiKeyInfo; + if (Authentication.AuthenticationType.API_KEY.equals(authentication.getAuthenticationType())) { + final String apiKeyId = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY); // mandatory + final String apiKeyName = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_NAME_KEY); // optional + apiKeyInfo = new AuthenticateResponse.ApiKeyInfo(apiKeyId, apiKeyName); + } else { + apiKeyInfo = null; + } return new AuthenticateResponse( cUser, user.enabled(), authenticatedBy, lookedUpBy, - authentication.getAuthenticationType().toString().toLowerCase(Locale.ROOT) + authentication.getAuthenticationType().toString().toLowerCase(Locale.ROOT), + null, + apiKeyInfo ); } } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityContext.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityContext.java index e78cb5392f619..4c1b992af7c4b 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityContext.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/SecurityContext.java @@ -21,6 +21,7 @@ import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer; import org.elasticsearch.xpack.core.security.authc.support.SecondaryAuthentication; import org.elasticsearch.xpack.core.security.user.User; @@ -35,8 +36,6 @@ import java.util.function.Function; import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; /** * A lightweight utility that can find the current user and authentication information for the local thread. @@ -190,23 +189,29 @@ private Map rewriteMetadataForApiKeyRoleDescriptors(Version stre && streamVersion.before(VERSION_API_KEY_ROLES_AS_BYTES)) { metadata = new HashMap<>(metadata); metadata.put( - API_KEY_ROLE_DESCRIPTORS_KEY, - convertRoleDescriptorsBytesToMap((BytesReference) metadata.get(API_KEY_ROLE_DESCRIPTORS_KEY)) + AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, + convertRoleDescriptorsBytesToMap((BytesReference) metadata.get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY)) ); metadata.put( - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - convertRoleDescriptorsBytesToMap((BytesReference) metadata.get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY)) + AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, + convertRoleDescriptorsBytesToMap( + (BytesReference) metadata.get(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY) + ) ); } else if (authentication.getVersion().before(VERSION_API_KEY_ROLES_AS_BYTES) && streamVersion.onOrAfter(VERSION_API_KEY_ROLES_AS_BYTES)) { metadata = new HashMap<>(metadata); metadata.put( - API_KEY_ROLE_DESCRIPTORS_KEY, - convertRoleDescriptorsMapToBytes((Map) metadata.get(API_KEY_ROLE_DESCRIPTORS_KEY)) + AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, + convertRoleDescriptorsMapToBytes( + (Map) metadata.get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY) + ) ); metadata.put( - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - convertRoleDescriptorsMapToBytes((Map) metadata.get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY)) + AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, + convertRoleDescriptorsMapToBytes( + (Map) metadata.get(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY) + ) ); } } 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 04a22501baebe..7170c9a346848 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 @@ -29,8 +29,6 @@ import java.util.Map; import java.util.Objects; -import static org.elasticsearch.xpack.core.security.authz.privilege.ManageOwnApiKeyClusterPrivilege.API_KEY_ID_KEY; - // TODO(hub-cap) Clean this up after moving User over - This class can re-inherit its field AUTHENTICATION_KEY in AuthenticationField. // That interface can be removed public class Authentication implements ToXContentObject { @@ -42,7 +40,7 @@ public class Authentication implements ToXContentObject { private final RealmRef lookedUpBy; private final Version version; private final AuthenticationType type; - private final Map metadata; + private final Map metadata; // authentication contains metadata, includes api_key details (including api_key metadata) public Authentication(User user, RealmRef authenticatedBy, RealmRef lookedUpBy) { this(user, authenticatedBy, lookedUpBy, Version.CURRENT); @@ -66,6 +64,7 @@ public Authentication( this.version = version; this.type = type; this.metadata = metadata; + this.assertApiKeyMetadata(); } public Authentication(StreamInput in) throws IOException { @@ -79,6 +78,7 @@ public Authentication(StreamInput in) throws IOException { this.version = in.getVersion(); type = AuthenticationType.values()[in.readVInt()]; metadata = in.readMap(); + this.assertApiKeyMetadata(); } public User getUser() { @@ -117,6 +117,10 @@ public boolean isServiceAccount() { return ServiceAccountSettings.REALM_TYPE.equals(getAuthenticatedBy().getType()) && null == getLookedUpBy(); } + public boolean isApiKey() { + return AuthenticationType.API_KEY.equals(getAuthenticationType()); + } + /** * Writes the authentication to the context. There must not be an existing authentication in the context and if there is an * {@link IllegalStateException} will be thrown @@ -163,7 +167,8 @@ public void writeTo(StreamOutput out) throws IOException { */ public boolean canAccessResourcesOf(Authentication other) { if (AuthenticationType.API_KEY == getAuthenticationType() && AuthenticationType.API_KEY == other.getAuthenticationType()) { - final boolean sameKeyId = getMetadata().get(API_KEY_ID_KEY).equals(other.getMetadata().get(API_KEY_ID_KEY)); + final boolean sameKeyId = getMetadata().get(AuthenticationField.API_KEY_ID_KEY) + .equals(other.getMetadata().get(AuthenticationField.API_KEY_ID_KEY)); if (sameKeyId) { assert getUser().principal().equals(other.getUser().principal()) : "The same API key ID cannot be attributed to two different usernames"; @@ -256,6 +261,23 @@ public void toXContentFragment(XContentBuilder builder) throws IOException { } builder.endObject(); builder.field(User.Fields.AUTHENTICATION_TYPE.getPreferredName(), getAuthenticationType().name().toLowerCase(Locale.ROOT)); + if (isApiKey()) { + this.assertApiKeyMetadata(); + final String apiKeyId = (String) this.metadata.get(AuthenticationField.API_KEY_ID_KEY); + final String apiKeyName = (String) this.metadata.get(AuthenticationField.API_KEY_NAME_KEY); + if (apiKeyName == null) { + builder.field("api_key", Map.of("id", apiKeyId)); + } else { + builder.field("api_key", Map.of("id", apiKeyId, "name", apiKeyName)); + } + } + } + + private void assertApiKeyMetadata() { + assert (AuthenticationType.API_KEY.equals(this.type) == false) || (this.metadata.get(AuthenticationField.API_KEY_ID_KEY) != null) + : "API KEY authentication requires metadata to contain API KEY id, and the value must be non-null."; + assert (AuthenticationType.API_KEY.equals(this.type) == false) || (this.metadata.containsKey(AuthenticationField.API_KEY_NAME_KEY)) + : "API KEY authentication requires metadata to contain API KEY name; the value may be null for older keys."; } @Override diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java index 99f49c0c5c956..7a8ab3b5df237 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/AuthenticationField.java @@ -9,11 +9,20 @@ public final class AuthenticationField { public static final String AUTHENTICATION_KEY = "_xpack_security_authentication"; - public static final String API_KEY_ROLE_DESCRIPTORS_KEY = "_security_api_key_role_descriptors"; - public static final String API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY = "_security_api_key_limited_by_role_descriptors"; public static final String PRIVILEGE_CATEGORY_KEY = "_security_privilege_category"; public static final String PRIVILEGE_CATEGORY_VALUE_OPERATOR = "operator"; public static final String PRIVILEGE_CATEGORY_VALUE_EMPTY = "__empty"; + public static final String API_KEY_REALM_NAME = "_es_api_key"; + public static final String API_KEY_REALM_TYPE = "_es_api_key"; + + public static final String API_KEY_CREATOR_REALM_NAME = "_security_api_key_creator_realm_name"; + public static final String API_KEY_CREATOR_REALM_TYPE = "_security_api_key_creator_realm_type"; + public static final String API_KEY_ID_KEY = "_security_api_key_id"; + public static final String API_KEY_NAME_KEY = "_security_api_key_name"; + public static final String API_KEY_METADATA_KEY = "_security_api_key_metadata"; + public static final String API_KEY_ROLE_DESCRIPTORS_KEY = "_security_api_key_role_descriptors"; + public static final String API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY = "_security_api_key_limited_by_role_descriptors"; + private AuthenticationField() {} } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilege.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilege.java index fd885ab3f20f1..104cef4ef9136 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilege.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilege.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission; import org.elasticsearch.xpack.core.security.support.Automatons; @@ -26,7 +27,6 @@ public class ManageOwnApiKeyClusterPrivilege implements NamedClusterPrivilege { public static final ManageOwnApiKeyClusterPrivilege INSTANCE = new ManageOwnApiKeyClusterPrivilege(); private static final String PRIVILEGE_NAME = "manage_own_api_key"; - public static final String API_KEY_ID_KEY = "_security_api_key_id"; private final ClusterPermission permission; @@ -137,7 +137,7 @@ private boolean checkIfUserIsOwnerOfApiKeys( private boolean isCurrentAuthenticationUsingSameApiKeyIdFromRequest(Authentication authentication, String apiKeyId) { if (AuthenticationType.API_KEY == authentication.getAuthenticationType()) { // API key id from authentication must match the id from request - final String authenticatedApiKeyId = (String) authentication.getMetadata().get(API_KEY_ID_KEY); + final String authenticatedApiKeyId = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY); if (Strings.hasText(apiKeyId)) { return apiKeyId.equals(authenticatedApiKeyId); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/DelegatePkiAuthenticationResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/DelegatePkiAuthenticationResponseTests.java index b539d0a46fec6..4ec42f9299c65 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/DelegatePkiAuthenticationResponseTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/action/DelegatePkiAuthenticationResponseTests.java @@ -17,6 +17,7 @@ import org.elasticsearch.xcontent.XContentParser; import org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse; import org.elasticsearch.xpack.core.security.authc.Authentication; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.user.User; import java.io.IOException; @@ -158,6 +159,10 @@ public static Authentication createAuthentication() { final String lookupRealmType = randomFrom("file", "native", "ldap", "active_directory", "saml", "kerberos"); final String nodeName = "node_name"; final Authentication.AuthenticationType authenticationType = randomFrom(Authentication.AuthenticationType.values()); + if (Authentication.AuthenticationType.API_KEY.equals(authenticationType)) { + metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10)); + } return new Authentication( new User(username, roles, fullName, email, metadata, true), new Authentication.RealmRef(authenticationRealmName, authenticationRealmType, nodeName), diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTests.java index 362fe09c8ab1a..bdc5d2656b58f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authc/AuthenticationTests.java @@ -19,11 +19,11 @@ import java.util.Arrays; import java.util.EnumSet; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.core.security.authz.privilege.ManageOwnApiKeyClusterPrivilege.API_KEY_ID_KEY; import static org.hamcrest.Matchers.is; public class AuthenticationTests extends ESTestCase { @@ -205,13 +205,16 @@ public static Authentication randomAuthentication(User user, RealmRef realmRef) public static Authentication randomApiKeyAuthentication(User user, String apiKeyId) { final RealmRef apiKeyRealm = new RealmRef("_es_api_key", "_es_api_key", randomAlphaOfLengthBetween(3, 8)); + final HashMap metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, apiKeyId); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16)); return new Authentication( user, apiKeyRealm, null, VersionUtils.randomVersionBetween(random(), Version.V_7_0_0, Version.CURRENT), AuthenticationType.API_KEY, - Map.of(API_KEY_ID_KEY, apiKeyId) + metadata ); } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilegeTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilegeTests.java index eda0e4e6b455c..8fda71fc22c6f 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilegeTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/privilege/ManageOwnApiKeyClusterPrivilegeTests.java @@ -17,9 +17,11 @@ import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authz.permission.ClusterPermission; import org.elasticsearch.xpack.core.security.user.User; +import java.util.HashMap; import java.util.Map; import static org.hamcrest.Matchers.is; @@ -33,12 +35,10 @@ public void testAuthenticationWithApiKeyAllowsAccessToApiKeyActionsWhenItIsOwner .build(); final String apiKeyId = randomAlphaOfLengthBetween(4, 7); - final Authentication authentication = createMockAuthentication( - "joe", - "_es_api_key", - AuthenticationType.API_KEY, - Map.of("_security_api_key_id", apiKeyId) - ); + final HashMap metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, apiKeyId); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomAlphaOfLengthBetween(1, 16)); + final Authentication authentication = createMockAuthentication("joe", "_es_api_key", AuthenticationType.API_KEY, metadata); final TransportRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean()); final TransportRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean()); @@ -52,12 +52,10 @@ public void testAuthenticationWithApiKeyDeniesAccessToApiKeyActionsWhenItIsNotOw .build(); final String apiKeyId = randomAlphaOfLengthBetween(4, 7); - final Authentication authentication = createMockAuthentication( - "joe", - "_es_api_key", - AuthenticationType.API_KEY, - Map.of("_security_api_key_id", randomAlphaOfLength(7)) - ); + final HashMap metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLength(7)); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16)); + final Authentication authentication = createMockAuthentication("joe", "_es_api_key", AuthenticationType.API_KEY, metadata); final TransportRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean()); final TransportRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean()); diff --git a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyRestIT.java b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyRestIT.java index 93fd8a437c424..c68a269f4bcd2 100644 --- a/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyRestIT.java +++ b/x-pack/plugin/security/qa/security-trial/src/javaRestTest/java/org/elasticsearch/xpack/security/apikey/ApiKeyRestIT.java @@ -29,10 +29,13 @@ import java.util.Set; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.lessThanOrEqualTo; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; /** @@ -63,6 +66,38 @@ public void cleanUp() throws IOException { invalidateApiKeysForUser(END_USER); } + @SuppressWarnings({ "unchecked" }) + public void testAuthenticateResponseApiKey() throws IOException { + final String expectedApiKeyName = "my-api-key-name"; + final Map expectedApiKeyMetadata = Map.of("not", "returned"); + final Map createApiKeyRequestBody = Map.of("name", expectedApiKeyName, "metadata", expectedApiKeyMetadata); + + final Request createApiKeyRequest = new Request("POST", "_security/api_key"); + createApiKeyRequest.setJsonEntity(XContentTestUtils.convertToXContent(createApiKeyRequestBody, XContentType.JSON).utf8ToString()); + + final Response createApiKeyResponse = adminClient().performRequest(createApiKeyRequest); + final Map createApiKeyResponseMap = responseAsMap(createApiKeyResponse); // keys: id, name, api_key, encoded + final String actualApiKeyId = (String) createApiKeyResponseMap.get("id"); + final String actualApiKeyName = (String) createApiKeyResponseMap.get("name"); + final String actualApiKeyEncoded = (String) createApiKeyResponseMap.get("encoded"); // Base64(id:api_key) + assertThat(actualApiKeyId, not(emptyString())); + assertThat(actualApiKeyName, equalTo(expectedApiKeyName)); + assertThat(actualApiKeyEncoded, not(emptyString())); + + final Request authenticateRequest = new Request("GET", "_security/_authenticate"); + authenticateRequest.setOptions( + authenticateRequest.getOptions().toBuilder().addHeader("Authorization", "ApiKey " + actualApiKeyEncoded) + ); + + final Response authenticateResponse = client().performRequest(authenticateRequest); + assertOK(authenticateResponse); + final Map authenticate = responseAsMap(authenticateResponse); // keys: username, roles, full_name, etc + + // If authentication type is API_KEY, authentication.api_key={"id":"abc123","name":"my-api-key"}. No encoded, api_key, or metadata. + // If authentication type is other, authentication.api_key not present. + assertThat(authenticate, hasEntry("api_key", Map.of("id", actualApiKeyId, "name", expectedApiKeyName))); + } + public void testGrantApiKeyForOtherUserWithPassword() throws IOException { Request request = new Request("POST", "_security/api_key/grant"); request.setOptions( diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/FileRealmAuthIT.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/FileRealmAuthIT.java index 7e9fe63f3a38b..1703a6e6fa083 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/FileRealmAuthIT.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/FileRealmAuthIT.java @@ -9,6 +9,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import java.io.IOException; @@ -32,6 +33,7 @@ public void testAuthenticationUsingFileRealm() throws IOException { assertUsername(authenticate, USERNAME); assertRealm(authenticate, "file", "file0"); assertRoles(authenticate, ROLE_NAME); + assertNoApiKeyInfo(authenticate, Authentication.AuthenticationType.REALM); } } diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/NativeRealmAuthIT.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/NativeRealmAuthIT.java index 3eed0e1abb4e8..50e9ea7641535 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/NativeRealmAuthIT.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/NativeRealmAuthIT.java @@ -9,6 +9,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.junit.After; import org.junit.Before; @@ -47,6 +48,7 @@ public void testAuthenticationUsingNativeRealm() throws IOException { assertUsername(authenticate, USERNAME); assertRealm(authenticate, "native", "native1"); assertRoles(authenticate, ROLE_NAME); + assertNoApiKeyInfo(authenticate, Authentication.AuthenticationType.REALM); } } diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java index c32dbbc07a8dc..69391ff14e73c 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/PkiRealmAuthIT.java @@ -10,6 +10,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.xpack.core.security.authc.Authentication; import java.io.IOException; import java.util.Map; @@ -39,6 +40,7 @@ public void testAuthenticationUsingPkiRealm() throws IOException { assertUsername(authenticate, USERNAME); assertRealm(authenticate, "pki", "pki4"); assertRoles(authenticate, new String[0]); + assertNoApiKeyInfo(authenticate, Authentication.AuthenticationType.REALM); } } diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/ReservedRealmAuthIT.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/ReservedRealmAuthIT.java index 7c99b7c31eba6..bb096dad8f151 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/ReservedRealmAuthIT.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/ReservedRealmAuthIT.java @@ -9,6 +9,7 @@ import org.elasticsearch.client.RequestOptions; import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken; import org.junit.Before; @@ -39,6 +40,7 @@ public void testAuthenticationUsingReservedRealm() throws IOException { assertUsername(authenticate, USERNAME); assertRealm(authenticate, "reserved", "reserved"); assertRoles(authenticate, ROLE_NAME); + assertNoApiKeyInfo(authenticate, Authentication.AuthenticationType.REALM); } } diff --git a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/SecurityRealmSmokeTestCase.java b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/SecurityRealmSmokeTestCase.java index c4a847e674d23..bdccd10db561c 100644 --- a/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/SecurityRealmSmokeTestCase.java +++ b/x-pack/plugin/security/qa/smoke-test-all-realms/src/javaRestTest/java/org/elasticsearch/xpack/security/authc/SecurityRealmSmokeTestCase.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.core.PathUtils; import org.elasticsearch.test.rest.ESRestTestCase; +import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; import org.junit.BeforeClass; import java.io.FileNotFoundException; @@ -38,7 +39,9 @@ import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasKey; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; @SuppressWarnings("removal") public abstract class SecurityRealmSmokeTestCase extends ESRestTestCase { @@ -101,6 +104,12 @@ protected void assertRoles(Map authenticateResponse, String... r ); } + protected void assertNoApiKeyInfo(Map authenticateResponse, AuthenticationType type) { + // If authentication type is API_KEY, authentication.api_key={"id":"abc123","name":"my-api-key"}. No encoded, api_key, or metadata. + // If authentication type is other, authentication.api_key not present. + assertThat(authenticateResponse, not(hasKey("api_key"))); + } + protected void createUser(String username, SecureString password, List roles) throws IOException { final RestHighLevelClient client = getHighLevelAdminClient(); client.security() diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java index 8609a49bc866c..38bd3956e39e6 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrail.java @@ -73,6 +73,7 @@ import org.elasticsearch.xpack.core.security.action.user.SetEnabledAction; import org.elasticsearch.xpack.core.security.action.user.SetEnabledRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo; @@ -1472,12 +1473,12 @@ LogEntryBuilder withAuthentication(Authentication authentication) { logEntry.with(PRINCIPAL_FIELD_NAME, authentication.getUser().principal()); logEntry.with(AUTHENTICATION_TYPE_FIELD_NAME, authentication.getAuthenticationType().toString()); if (Authentication.AuthenticationType.API_KEY == authentication.getAuthenticationType()) { - logEntry.with(API_KEY_ID_FIELD_NAME, (String) authentication.getMetadata().get(ApiKeyService.API_KEY_ID_KEY)); - String apiKeyName = (String) authentication.getMetadata().get(ApiKeyService.API_KEY_NAME_KEY); + logEntry.with(API_KEY_ID_FIELD_NAME, (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY)); + String apiKeyName = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_NAME_KEY); if (apiKeyName != null) { logEntry.with(API_KEY_NAME_FIELD_NAME, apiKeyName); } - String creatorRealmName = (String) authentication.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_NAME); + String creatorRealmName = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_CREATOR_REALM_NAME); if (creatorRealmName != null) { // can be null for API keys created before version 7.7 logEntry.with(PRINCIPAL_REALM_FIELD_NAME, creatorRealmName); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java index 2018b667fbd8f..4efbb10db1601 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java @@ -87,6 +87,7 @@ import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyResponse; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; @@ -137,8 +138,6 @@ import static org.elasticsearch.xpack.core.ClientHelper.executeAsyncWithOrigin; import static org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; import static org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames.SECURITY_MAIN_ALIAS; import static org.elasticsearch.xpack.security.Security.SECURITY_CRYPTO_THREAD_POOL_NAME; @@ -146,13 +145,6 @@ public class ApiKeyService { private static final Logger logger = LogManager.getLogger(ApiKeyService.class); private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(ApiKeyService.class); - public static final String API_KEY_ID_KEY = "_security_api_key_id"; - public static final String API_KEY_NAME_KEY = "_security_api_key_name"; - public static final String API_KEY_METADATA_KEY = "_security_api_key_metadata"; - public static final String API_KEY_REALM_NAME = "_es_api_key"; - public static final String API_KEY_REALM_TYPE = "_es_api_key"; - public static final String API_KEY_CREATOR_REALM_NAME = "_security_api_key_creator_realm_name"; - public static final String API_KEY_CREATOR_REALM_TYPE = "_security_api_key_creator_realm_type"; public static final Setting PASSWORD_HASHING_ALGORITHM = new Setting<>( "xpack.security.authc.api_key.hashing.algorithm", @@ -457,7 +449,11 @@ public Authentication createApiKeyAuthentication(AuthenticationResult auth throw new IllegalArgumentException("API Key authn result must be successful"); } final User user = authResult.getValue(); - final RealmRef authenticatedBy = new RealmRef(ApiKeyService.API_KEY_REALM_NAME, ApiKeyService.API_KEY_REALM_TYPE, nodeName); + final RealmRef authenticatedBy = new RealmRef( + AuthenticationField.API_KEY_REALM_NAME, + AuthenticationField.API_KEY_REALM_TYPE, + nodeName + ); return new Authentication( user, authenticatedBy, @@ -552,11 +548,13 @@ public void getRoleForApiKey(Authentication authentication, ActionListener metadata = authentication.getMetadata(); - final String apiKeyId = (String) metadata.get(API_KEY_ID_KEY); + final String apiKeyId = (String) metadata.get(AuthenticationField.API_KEY_ID_KEY); @SuppressWarnings("unchecked") - final Map roleDescriptors = (Map) metadata.get(API_KEY_ROLE_DESCRIPTORS_KEY); + final Map roleDescriptors = (Map) metadata.get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY); @SuppressWarnings("unchecked") - final Map authnRoleDescriptors = (Map) metadata.get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY); + final Map authnRoleDescriptors = (Map) metadata.get( + AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY + ); if (roleDescriptors == null && authnRoleDescriptors == null) { listener.onFailure(new ElasticsearchSecurityException("no role descriptors found for API key")); @@ -579,15 +577,15 @@ public Tuple getApiKeyIdAndRoleBytes(Authentication auth final Map metadata = authentication.getMetadata(); final BytesReference bytesReference = (BytesReference) metadata.get( - limitedBy ? API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY : API_KEY_ROLE_DESCRIPTORS_KEY + limitedBy ? AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY : AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY ); if (limitedBy && bytesReference.length() == 2 && "{}".equals(bytesReference.utf8ToString())) { - if (ServiceAccountSettings.REALM_NAME.equals(metadata.get(API_KEY_CREATOR_REALM_NAME)) + if (ServiceAccountSettings.REALM_NAME.equals(metadata.get(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) && "elastic/fleet-server".equals(authentication.getUser().principal())) { - return new Tuple<>((String) metadata.get(API_KEY_ID_KEY), FLEET_SERVER_ROLE_DESCRIPTOR_BYTES_V_7_14); + return new Tuple<>((String) metadata.get(AuthenticationField.API_KEY_ID_KEY), FLEET_SERVER_ROLE_DESCRIPTOR_BYTES_V_7_14); } } - return new Tuple<>((String) metadata.get(API_KEY_ID_KEY), bytesReference); + return new Tuple<>((String) metadata.get(AuthenticationField.API_KEY_ID_KEY), bytesReference); } public static class ApiKeyRoleDescriptors { @@ -785,14 +783,14 @@ void validateApiKeyExpiration( Map metadata = (Map) apiKeyDoc.creator.get("metadata"); final User apiKeyUser = new User(principal, Strings.EMPTY_ARRAY, fullName, email, metadata, true); final Map 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")); - authResultMetadata.put(API_KEY_ROLE_DESCRIPTORS_KEY, apiKeyDoc.roleDescriptorsBytes); - authResultMetadata.put(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, apiKeyDoc.limitedByRoleDescriptorsBytes); - authResultMetadata.put(API_KEY_ID_KEY, credentials.getId()); - authResultMetadata.put(API_KEY_NAME_KEY, apiKeyDoc.name); + authResultMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_NAME, apiKeyDoc.creator.get("realm")); + authResultMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_TYPE, apiKeyDoc.creator.get("realm_type")); + authResultMetadata.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, apiKeyDoc.roleDescriptorsBytes); + authResultMetadata.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, apiKeyDoc.limitedByRoleDescriptorsBytes); + authResultMetadata.put(AuthenticationField.API_KEY_ID_KEY, credentials.getId()); + authResultMetadata.put(AuthenticationField.API_KEY_NAME_KEY, apiKeyDoc.name); if (apiKeyDoc.metadataFlattened != null) { - authResultMetadata.put(API_KEY_METADATA_KEY, apiKeyDoc.metadataFlattened); + authResultMetadata.put(AuthenticationField.API_KEY_METADATA_KEY, apiKeyDoc.metadataFlattened); } listener.onResponse(AuthenticationResult.success(apiKeyUser, authResultMetadata)); } else { @@ -841,7 +839,7 @@ ApiKeyCredentials getCredentialsFromHeader(ThreadContext threadContext) { public static boolean isApiKeyAuthentication(Authentication authentication) { final Authentication.AuthenticationType authType = authentication.getAuthenticationType(); if (Authentication.AuthenticationType.API_KEY == authType) { - assert API_KEY_REALM_TYPE.equals(authentication.getAuthenticatedBy().getType()) + assert AuthenticationField.API_KEY_REALM_TYPE.equals(authentication.getAuthenticatedBy().getType()) : "API key authentication must have API key realm type"; return true; } else { @@ -1397,14 +1395,15 @@ AtomicLong getLastEvictionCheckedAt() { /** * Returns realm name for the authenticated user. - * If the user is authenticated by realm type {@value API_KEY_REALM_TYPE} + * If the user is authenticated by realm type {@value AuthenticationField#API_KEY_REALM_TYPE} * then it will return the realm name of user who created this API key. + * * @param authentication {@link Authentication} * @return realm name */ public static String getCreatorRealmName(final Authentication authentication) { if (AuthenticationType.API_KEY == authentication.getAuthenticationType()) { - return (String) authentication.getMetadata().get(API_KEY_CREATOR_REALM_NAME); + return (String) authentication.getMetadata().get(AuthenticationField.API_KEY_CREATOR_REALM_NAME); } else { return authentication.getSourceRealm().getName(); } @@ -1412,14 +1411,15 @@ public static String getCreatorRealmName(final Authentication authentication) { /** * Returns realm type for the authenticated user. - * If the user is authenticated by realm type {@value API_KEY_REALM_TYPE} + * If the user is authenticated by realm type {@value AuthenticationField#API_KEY_REALM_TYPE} * then it will return the realm name of user who created this API key. + * * @param authentication {@link Authentication} * @return realm type */ public static String getCreatorRealmType(final Authentication authentication) { if (AuthenticationType.API_KEY == authentication.getAuthenticationType()) { - return (String) authentication.getMetadata().get(API_KEY_CREATOR_REALM_TYPE); + return (String) authentication.getMetadata().get(AuthenticationField.API_KEY_CREATOR_REALM_TYPE); } else { return authentication.getSourceRealm().getType(); } @@ -1439,7 +1439,7 @@ public static Map getApiKeyMetadata(Authentication authenticatio + "]" ); } - final Object apiKeyMetadata = authentication.getMetadata().get(ApiKeyService.API_KEY_METADATA_KEY); + final Object apiKeyMetadata = authentication.getMetadata().get(AuthenticationField.API_KEY_METADATA_KEY); if (apiKeyMetadata != null) { final Tuple> tuple = XContentHelper.convertToMap( (BytesReference) apiKeyMetadata, diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index 72b95efd1ea4e..6aee449f60d16 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -52,6 +52,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; import org.elasticsearch.xpack.core.security.authc.AuthenticationFailureHandler; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.esnative.ClientReservedRealm; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AsyncSupplier; @@ -75,7 +76,6 @@ import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditTrailService; import org.elasticsearch.xpack.security.audit.AuditUtil; -import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authz.interceptor.RequestInterceptor; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; import org.elasticsearch.xpack.security.operator.OperatorPrivileges.OperatorPrivilegesService; @@ -889,7 +889,7 @@ private ElasticsearchSecurityException denialException( } // check for authentication by API key if (AuthenticationType.API_KEY == authentication.getAuthenticationType()) { - final String apiKeyId = (String) authentication.getMetadata().get(ApiKeyService.API_KEY_ID_KEY); + final String apiKeyId = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY); assert apiKeyId != null : "api key id must be present in the metadata"; userText = "API key id [" + apiKeyId + "] of " + userText; } else if (false == authentication.isServiceAccount()) { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java index 55cec41983ae1..7bd64f4d286bc 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java @@ -54,6 +54,7 @@ import org.elasticsearch.xpack.core.security.action.user.UserRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine; import org.elasticsearch.xpack.core.security.authz.ResolvedIndices; @@ -75,7 +76,6 @@ import org.elasticsearch.xpack.core.security.support.StringMatcher; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.core.sql.SqlAsyncActionNames; -import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; @@ -205,7 +205,7 @@ boolean checkSameUserPermissions(String action, TransportRequest request, Authen if (AuthenticationType.API_KEY == authentication.getAuthenticationType()) { assert authentication.getLookedUpBy() == null : "runAs not supported for api key authentication"; // if authenticated by API key then the request must also contain same API key id - String authenticatedApiKeyId = (String) authentication.getMetadata().get(ApiKeyService.API_KEY_ID_KEY); + String authenticatedApiKeyId = (String) authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY); if (Strings.hasText(getApiKeyRequest.getApiKeyId())) { return getApiKeyRequest.getApiKeyId().equals(authenticatedApiKeyId); } else { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java index c44f35ab57921..fcf21acc01f19 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessor.java @@ -15,6 +15,7 @@ import org.elasticsearch.xpack.core.XPackSettings; import org.elasticsearch.xpack.core.security.SecurityContext; import org.elasticsearch.xpack.core.security.authc.Authentication; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authc.ApiKeyService; @@ -146,13 +147,11 @@ public IngestDocument execute(IngestDocument ingestDocument) throws Exception { final Map apiKeyField = existingApiKeyField instanceof Map ? (Map) existingApiKeyField : new HashMap<>(); - Object apiKeyName = authentication.getMetadata().get(ApiKeyService.API_KEY_NAME_KEY); - if (apiKeyName != null) { - apiKeyField.put("name", apiKeyName); + if (authentication.getMetadata().containsKey(AuthenticationField.API_KEY_NAME_KEY)) { + apiKeyField.put("name", authentication.getMetadata().get(AuthenticationField.API_KEY_NAME_KEY)); } - Object apiKeyId = authentication.getMetadata().get(ApiKeyService.API_KEY_ID_KEY); - if (apiKeyId != null) { - apiKeyField.put("id", apiKeyId); + if (authentication.getMetadata().containsKey(AuthenticationField.API_KEY_ID_KEY)) { + apiKeyField.put("id", authentication.getMetadata().get(AuthenticationField.API_KEY_ID_KEY)); } final Map apiKeyMetadata = ApiKeyService.getApiKeyMetadata(authentication); if (false == apiKeyMetadata.isEmpty()) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityContextTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityContextTests.java index f5e9255baf950..c718c80d43443 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityContextTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/SecurityContextTests.java @@ -27,13 +27,12 @@ import java.io.EOFException; import java.io.IOException; import java.io.UncheckedIOException; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.elasticsearch.xpack.core.security.authc.Authentication.VERSION_API_KEY_ROLES_AS_BYTES; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; @@ -157,10 +156,12 @@ public void testExecuteAfterRewritingAuthentication() throws IOException { public void testExecuteAfterRewritingAuthenticationWillConditionallyRewriteNewApiKeyMetadata() throws IOException { User user = new User("test", null, new User("authUser")); RealmRef authBy = new RealmRef("_es_api_key", "_es_api_key", "node1"); - final Map metadata = Map.of( - API_KEY_ROLE_DESCRIPTORS_KEY, - new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}"), - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, + final Map metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}")); + metadata.put( + AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, new BytesArray("{\"limitedBy role\": {\"cluster\": [\"all\"]}}") ); final Authentication original = new Authentication(user, authBy, authBy, Version.V_8_0_0, AuthenticationType.API_KEY, metadata); @@ -171,11 +172,11 @@ public void testExecuteAfterRewritingAuthenticationWillConditionallyRewriteNewAp Authentication authentication = securityContext.getAuthentication(); assertEquals( Map.of("a role", Map.of("cluster", List.of("all"))), - authentication.getMetadata().get(API_KEY_ROLE_DESCRIPTORS_KEY) + authentication.getMetadata().get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY) ); assertEquals( Map.of("limitedBy role", Map.of("cluster", List.of("all"))), - authentication.getMetadata().get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY) + authentication.getMetadata().get(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY) ); }, Version.V_7_8_0); @@ -189,12 +190,11 @@ public void testExecuteAfterRewritingAuthenticationWillConditionallyRewriteNewAp public void testExecuteAfterRewritingAuthenticationWillConditionallyRewriteOldApiKeyMetadata() throws IOException { User user = new User("test", null, new User("authUser")); RealmRef authBy = new RealmRef("_es_api_key", "_es_api_key", "node1"); - final Map metadata = Map.of( - API_KEY_ROLE_DESCRIPTORS_KEY, - Map.of("a role", Map.of("cluster", List.of("all"))), - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - Map.of("limitedBy role", Map.of("cluster", List.of("all"))) - ); + final Map metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 10)); + metadata.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, Map.of("a role", Map.of("cluster", List.of("all")))); + metadata.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, Map.of("limitedBy role", Map.of("cluster", List.of("all")))); final Authentication original = new Authentication(user, authBy, authBy, Version.V_7_8_0, AuthenticationType.API_KEY, metadata); original.writeToContext(threadContext); @@ -209,11 +209,11 @@ public void testExecuteAfterRewritingAuthenticationWillConditionallyRewriteOldAp Authentication authentication = securityContext.getAuthentication(); assertEquals( "{\"a role\":{\"cluster\":[\"all\"]}}", - ((BytesReference) authentication.getMetadata().get(API_KEY_ROLE_DESCRIPTORS_KEY)).utf8ToString() + ((BytesReference) authentication.getMetadata().get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY)).utf8ToString() ); assertEquals( "{\"limitedBy role\":{\"cluster\":[\"all\"]}}", - ((BytesReference) authentication.getMetadata().get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY)).utf8ToString() + ((BytesReference) authentication.getMetadata().get(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY)).utf8ToString() ); }, VersionUtils.randomVersionBetween(random(), VERSION_API_KEY_ROLES_AS_BYTES, Version.CURRENT)); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java index a4486e2ff2be2..46c999138365c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailFilterTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.xpack.core.security.audit.logfile.CapturingLogger; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo; import org.elasticsearch.xpack.core.security.user.SystemUser; @@ -1315,7 +1316,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, filteredRealm)); auditTrail.accessGranted(randomAlphaOfLength(8), authentication, "_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessGranted message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1332,7 +1333,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, unfilteredRealm)); auditTrail.accessGranted(randomAlphaOfLength(8), authentication, "_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessGranted message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1371,7 +1372,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, filteredRealm)); auditTrail.accessGranted(randomAlphaOfLength(8), authentication, "internal:_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessGranted internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1388,7 +1389,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, unfilteredRealm)); auditTrail.accessGranted(randomAlphaOfLength(8), authentication, "internal:_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessGranted internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1406,7 +1407,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, filteredRealm)); auditTrail.accessDenied(randomAlphaOfLength(8), authentication, "_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessDenied message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1423,7 +1424,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, unfilteredRealm)); auditTrail.accessDenied(randomAlphaOfLength(8), authentication, "_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessDenied message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1462,7 +1463,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, filteredRealm)); auditTrail.accessDenied(randomAlphaOfLength(8), authentication, "internal:_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessDenied internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1479,7 +1480,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, unfilteredRealm)); auditTrail.accessDenied(randomAlphaOfLength(8), authentication, "internal:_action", request, authzInfo(new String[] { "role1" })); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("AccessDenied internal message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1515,7 +1516,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, filteredRealm)); auditTrail.tamperedRequest(randomAlphaOfLength(8), authentication, "_action", request); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("Tampered message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { @@ -1532,7 +1533,7 @@ public void testRealmsFilter() throws Exception { : createApiKeyAuthentication(apiKeyService, createAuthentication(user, unfilteredRealm)); auditTrail.tamperedRequest(randomAlphaOfLength(8), authentication, "_action", request); if (authentication.getAuthenticationType() == Authentication.AuthenticationType.API_KEY - && false == authentication.getMetadata().containsKey(ApiKeyService.API_KEY_CREATOR_REALM_NAME)) { + && false == authentication.getMetadata().containsKey(AuthenticationField.API_KEY_CREATOR_REALM_NAME)) { if (filterMissingRealm) { assertThat("Tampered message: not filtered out by the missing realm filter", logOutput.size(), is(0)); } else { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java index e9163f783b960..beba1abb6c0c7 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/audit/logfile/LoggingAuditTrailTests.java @@ -78,6 +78,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.AuthenticationToken; import org.elasticsearch.xpack.core.security.authc.service.ServiceAccountSettings; import org.elasticsearch.xpack.core.security.authc.support.mapper.TemplateRoleName; @@ -2604,13 +2605,13 @@ private static void authentication(Authentication authentication, MapBuilder(); @@ -524,9 +526,12 @@ public void testValidateApiKey() throws Exception { assertThat(result.getValue().email(), is("test@user.com")); assertThat(result.getValue().roles(), is(emptyArray())); assertThat(result.getValue().metadata(), is(Collections.emptyMap())); - assertThat(result.getMetadata().get(API_KEY_ROLE_DESCRIPTORS_KEY), equalTo(apiKeyDoc.roleDescriptorsBytes)); - assertThat(result.getMetadata().get(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY), equalTo(apiKeyDoc.limitedByRoleDescriptorsBytes)); - assertThat(result.getMetadata().get(ApiKeyService.API_KEY_CREATOR_REALM_NAME), is("realm1")); + assertThat(result.getMetadata().get(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY), equalTo(apiKeyDoc.roleDescriptorsBytes)); + assertThat( + result.getMetadata().get(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY), + equalTo(apiKeyDoc.limitedByRoleDescriptorsBytes) + ); + assertThat(result.getMetadata().get(AuthenticationField.API_KEY_CREATOR_REALM_NAME), is("realm1")); apiKeyDoc = buildApiKeyDoc(hash, Clock.systemUTC().instant().minus(1L, ChronoUnit.HOURS).toEpochMilli(), false); future = new PlainActionFuture<>(); @@ -570,10 +575,14 @@ public void testGetRolesForApiKeyNotInContext() throws Exception { ); } Map authMetadata = new HashMap<>(); - authMetadata.put(ApiKeyService.API_KEY_ID_KEY, randomAlphaOfLength(12)); - authMetadata.put(API_KEY_ROLE_DESCRIPTORS_KEY, Collections.singletonMap(SUPERUSER_ROLE_DESCRIPTOR.getName(), superUserRdMap)); + authMetadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLength(12)); + authMetadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLength(12)); + authMetadata.put( + AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, + Collections.singletonMap(SUPERUSER_ROLE_DESCRIPTOR.getName(), superUserRdMap) + ); authMetadata.put( - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, + AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, Collections.singletonMap(SUPERUSER_ROLE_DESCRIPTOR.getName(), superUserRdMap) ); @@ -597,7 +606,8 @@ public void testGetRolesForApiKeyNotInContext() throws Exception { @SuppressWarnings("unchecked") public void testGetRolesForApiKey() throws Exception { Map authMetadata = new HashMap<>(); - authMetadata.put(ApiKeyService.API_KEY_ID_KEY, randomAlphaOfLength(12)); + authMetadata.put(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLength(12)); + authMetadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLength(12)); boolean emptyApiKeyRoleDescriptor = randomBoolean(); final RoleDescriptor roleARoleDescriptor = new RoleDescriptor( "a role", @@ -615,7 +625,7 @@ public void testGetRolesForApiKey() throws Exception { ); } authMetadata.put( - API_KEY_ROLE_DESCRIPTORS_KEY, + AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, (emptyApiKeyRoleDescriptor) ? randomFrom(Arrays.asList(null, Collections.emptyMap())) : Collections.singletonMap("a role", roleARDMap) @@ -635,7 +645,7 @@ public void testGetRolesForApiKey() throws Exception { false ); } - authMetadata.put(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, Collections.singletonMap("limited role", limitedRdMap)); + authMetadata.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, Collections.singletonMap("limited role", limitedRdMap)); final Authentication authentication = new Authentication( new User("joe"), @@ -675,11 +685,12 @@ public void testGetRolesForApiKey() throws Exception { public void testGetApiKeyIdAndRoleBytes() { Map authMetadata = new HashMap<>(); final String apiKeyId = randomAlphaOfLength(12); - authMetadata.put(ApiKeyService.API_KEY_ID_KEY, apiKeyId); + authMetadata.put(AuthenticationField.API_KEY_ID_KEY, apiKeyId); + authMetadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLength(12)); final BytesReference roleBytes = new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}"); final BytesReference limitedByRoleBytes = new BytesArray("{\"limitedBy role\": {\"cluster\": [\"all\"]}}"); - authMetadata.put(API_KEY_ROLE_DESCRIPTORS_KEY, roleBytes); - authMetadata.put(API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, limitedByRoleBytes); + authMetadata.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, roleBytes); + authMetadata.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, limitedByRoleBytes); final Authentication authentication = new Authentication( new User("joe"), @@ -1510,14 +1521,14 @@ public static Authentication createApiKeyAuthentication( // maybe remove realm name to simulate old API Key authentication assert authenticationResult.getStatus() == AuthenticationResult.Status.SUCCESS; Map authenticationResultMetadata = new HashMap<>(authenticationResult.getMetadata()); - authenticationResultMetadata.remove(ApiKeyService.API_KEY_CREATOR_REALM_NAME); + authenticationResultMetadata.remove(AuthenticationField.API_KEY_CREATOR_REALM_NAME); authenticationResult = AuthenticationResult.success(authenticationResult.getValue(), authenticationResultMetadata); } if (randomBoolean()) { // simulate authentication with nameless API Key, see https://github.com/elastic/elasticsearch/issues/59484 assert authenticationResult.getStatus() == AuthenticationResult.Status.SUCCESS; Map authenticationResultMetadata = new HashMap<>(authenticationResult.getMetadata()); - authenticationResultMetadata.remove(ApiKeyService.API_KEY_NAME_KEY); + authenticationResultMetadata.put(AuthenticationField.API_KEY_NAME_KEY, null); authenticationResult = AuthenticationResult.success(authenticationResult.getValue(), authenticationResultMetadata); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java index 76108ab4a547b..6bd2741afce5f 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/RBACEngineTests.java @@ -48,6 +48,7 @@ import org.elasticsearch.xpack.core.security.action.user.UserRequest; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.esnative.NativeRealmSettings; import org.elasticsearch.xpack.core.security.authc.file.FileRealmSettings; import org.elasticsearch.xpack.core.security.authc.ldap.LdapRealmSettings; @@ -65,7 +66,6 @@ import org.elasticsearch.xpack.core.security.authz.privilege.Privilege; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm; import org.elasticsearch.xpack.security.authz.RBACEngine.RBACAuthorizationInfo; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; @@ -257,7 +257,7 @@ public void testSameUserPermissionDoesNotAllowChangePasswordForApiKey() { when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); when(authentication.getAuthenticationType()).thenReturn(Authentication.AuthenticationType.API_KEY); - when(authenticatedBy.getType()).thenReturn(ApiKeyService.API_KEY_REALM_TYPE); + when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE); assertThat(request, instanceOf(UserRequest.class)); assertFalse(engine.checkSameUserPermissions(action, request, authentication)); @@ -328,7 +328,7 @@ public void testSameUserPermissionAllowsSelfApiKeyInfoRetrievalWhenAuthenticated when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY); - when(authentication.getMetadata()).thenReturn(Map.of(ApiKeyService.API_KEY_ID_KEY, apiKeyId)); + when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, apiKeyId)); assertTrue(engine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication)); } @@ -341,8 +341,8 @@ public void testSameUserPermissionDeniesApiKeyInfoRetrievalWhenAuthenticatedByAD final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class); when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); - when(authenticatedBy.getType()).thenReturn(ApiKeyService.API_KEY_REALM_TYPE); - when(authentication.getMetadata()).thenReturn(Map.of(ApiKeyService.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); + when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE); + when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); assertFalse(engine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication)); } @@ -358,7 +358,7 @@ public void testSameUserPermissionDeniesApiKeyInfoRetrievalWhenLookedupByIsPrese when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); when(authentication.getLookedUpBy()).thenReturn(lookedupBy); when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY); - when(authentication.getMetadata()).thenReturn(Map.of(ApiKeyService.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); + when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); final AssertionError assertionError = expectThrows( AssertionError.class, diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 4df4270c46c4d..154ce73cbb69d 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges; import org.elasticsearch.xpack.core.security.authz.accesscontrol.DocumentSubsetBitsetCache; @@ -111,10 +112,6 @@ import static org.elasticsearch.test.ActionListenerUtils.anyActionListener; import static org.elasticsearch.xpack.core.security.SecurityField.DOCUMENT_LEVEL_SECURITY_FEATURE; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY; -import static org.elasticsearch.xpack.core.security.authc.AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY; -import static org.elasticsearch.xpack.core.security.test.TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON; -import static org.elasticsearch.xpack.security.authc.ApiKeyService.API_KEY_ID_KEY; import static org.elasticsearch.xpack.security.authc.ApiKeyServiceTests.Utils.createApiKeyAuthentication; import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -651,7 +648,7 @@ public void testMergingRolesWithFls() { Sets.newHashSet(flsRole, addsL1Fields), cache, null, - RESTRICTED_INDICES_AUTOMATON, + TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON, future ); Role role = future.actionGet(); @@ -759,7 +756,7 @@ public ClusterPermission.Builder buildPermission(ClusterPermission.Builder build Sets.newHashSet(role1, role2), cache, privilegeStore, - RESTRICTED_INDICES_AUTOMATON, + TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON, future ); Role role = future.actionGet(); @@ -1529,20 +1526,18 @@ public void testCacheEntryIsReusedForIdenticalApiKeyRoles() { AuditUtil.getOrGenerateRequestId(threadContext); final BytesArray roleBytes = new BytesArray("{\"a role\": {\"cluster\": [\"all\"]}}"); final BytesArray limitedByRoleBytes = new BytesArray("{\"limitedBy role\": {\"cluster\": [\"all\"]}}"); + final Map metadata = new HashMap<>(); + metadata.put(AuthenticationField.API_KEY_ID_KEY, "key-id-1"); + metadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16)); + metadata.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, roleBytes); + metadata.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, limitedByRoleBytes); Authentication authentication = new Authentication( new User("test api key user", "superuser"), new RealmRef("_es_api_key", "_es_api_key", "node"), null, Version.CURRENT, AuthenticationType.API_KEY, - Map.of( - API_KEY_ID_KEY, - "key-id-1", - API_KEY_ROLE_DESCRIPTORS_KEY, - roleBytes, - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - limitedByRoleBytes - ) + metadata ); doCallRealMethod().when(apiKeyService).getApiKeyIdAndRoleBytes(eq(authentication), anyBoolean()); @@ -1555,20 +1550,18 @@ public void testCacheEntryIsReusedForIdenticalApiKeyRoles() { verify(apiKeyService).parseRoleDescriptors("key-id-1", limitedByRoleBytes); // Different API key with the same roles should read from cache + final Map metadata2 = new HashMap<>(); + metadata2.put(AuthenticationField.API_KEY_ID_KEY, "key-id-2"); + metadata2.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16)); + metadata2.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, roleBytes); + metadata2.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, limitedByRoleBytes); authentication = new Authentication( new User("test api key user 2", "superuser"), new RealmRef("_es_api_key", "_es_api_key", "node"), null, Version.CURRENT, AuthenticationType.API_KEY, - Map.of( - API_KEY_ID_KEY, - "key-id-2", - API_KEY_ROLE_DESCRIPTORS_KEY, - roleBytes, - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - limitedByRoleBytes - ) + metadata2 ); doCallRealMethod().when(apiKeyService).getApiKeyIdAndRoleBytes(eq(authentication), anyBoolean()); roleFuture = new PlainActionFuture<>(); @@ -1580,20 +1573,18 @@ public void testCacheEntryIsReusedForIdenticalApiKeyRoles() { // Different API key with the same limitedBy role should read from cache, new role should be built final BytesArray anotherRoleBytes = new BytesArray("{\"b role\": {\"cluster\": [\"manage_security\"]}}"); + final Map metadata3 = new HashMap<>(); + metadata3.put(AuthenticationField.API_KEY_ID_KEY, "key-id-3"); + metadata3.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : randomAlphaOfLengthBetween(1, 16)); + metadata3.put(AuthenticationField.API_KEY_ROLE_DESCRIPTORS_KEY, anotherRoleBytes); + metadata3.put(AuthenticationField.API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, limitedByRoleBytes); authentication = new Authentication( new User("test api key user 2", "superuser"), new RealmRef("_es_api_key", "_es_api_key", "node"), null, Version.CURRENT, AuthenticationType.API_KEY, - Map.of( - API_KEY_ID_KEY, - "key-id-3", - API_KEY_ROLE_DESCRIPTORS_KEY, - anotherRoleBytes, - API_KEY_LIMITED_ROLE_DESCRIPTORS_KEY, - limitedByRoleBytes - ) + metadata3 ); doCallRealMethod().when(apiKeyService).getApiKeyIdAndRoleBytes(eq(authentication), anyBoolean()); roleFuture = new PlainActionFuture<>(); @@ -1632,7 +1623,7 @@ private Authentication createAuthentication() { } public void testXPackUserCanAccessNonRestrictedIndices() { - CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(RESTRICTED_INDICES_AUTOMATON); + CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON); for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getXPackUserRole().indices().allowedIndicesMatcher(action); IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); @@ -1672,7 +1663,7 @@ public void testXPackUserCannotWriteToAuditTrail() { } public void testAsyncSearchUserCannotAccessNonRestrictedIndices() { - CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(RESTRICTED_INDICES_AUTOMATON); + CharacterRunAutomaton restrictedAutomaton = new CharacterRunAutomaton(TestRestrictedIndices.RESTRICTED_INDICES_AUTOMATON); for (String action : Arrays.asList(GetAction.NAME, DeleteAction.NAME, SearchAction.NAME, IndexAction.NAME)) { Predicate predicate = getAsyncSearchUserRole().indices().allowedIndicesMatcher(action); IndexAbstraction index = mockIndexAbstraction(randomAlphaOfLengthBetween(3, 12)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java index 4027a0e71d2ea..2f3e26e72850e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/ingest/SetSecurityUserProcessorTests.java @@ -20,10 +20,10 @@ import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.authc.Authentication; import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType; +import org.elasticsearch.xpack.core.security.authc.AuthenticationField; import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer; import org.elasticsearch.xpack.core.security.support.ValidationTests; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.security.authc.ApiKeyService; import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property; import org.junit.Before; import org.mockito.Mockito; @@ -316,26 +316,22 @@ public void testOverwriteExistingField() throws Exception { public void testApiKeyPopulation() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12), null, null); Authentication.RealmRef realmRef = new Authentication.RealmRef( - ApiKeyService.API_KEY_REALM_NAME, - ApiKeyService.API_KEY_REALM_TYPE, + AuthenticationField.API_KEY_REALM_NAME, + AuthenticationField.API_KEY_REALM_TYPE, "_node_name" ); - final Map authMetadata = new HashMap<>( - Map.of( - ApiKeyService.API_KEY_ID_KEY, - "api_key_id", - ApiKeyService.API_KEY_NAME_KEY, - "api_key_name", - ApiKeyService.API_KEY_CREATOR_REALM_NAME, - "creator_realm_name", - ApiKeyService.API_KEY_CREATOR_REALM_TYPE, - "creator_realm_type" - ) - ); + final Map authMetadata = new HashMap<>(); + authMetadata.put(AuthenticationField.API_KEY_ID_KEY, "api_key_id"); + authMetadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : "api_key_name"); + authMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_NAME, "creator_realm_name"); + authMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_TYPE, "creator_realm_type"); final Map apiKeyMetadata = ApiKeyTests.randomMetadata(); if (apiKeyMetadata != null) { - authMetadata.put(ApiKeyService.API_KEY_METADATA_KEY, XContentTestUtils.convertToXContent(apiKeyMetadata, XContentType.JSON)); + authMetadata.put( + AuthenticationField.API_KEY_METADATA_KEY, + XContentTestUtils.convertToXContent(apiKeyMetadata, XContentType.JSON) + ); } Authentication auth = new Authentication(user, realmRef, null, Version.CURRENT, AuthenticationType.API_KEY, authMetadata); @@ -355,8 +351,9 @@ public void testApiKeyPopulation() throws Exception { Map result = ingestDocument.getFieldValue("_field", Map.class); assertThat(result, aMapWithSize(4)); final Map apiKeyMap = (Map) result.get("api_key"); - assertThat(apiKeyMap.get("name"), equalTo("api_key_name")); - assertThat(apiKeyMap.get("id"), equalTo("api_key_id")); + assertThat(apiKeyMap.get("id"), equalTo(authMetadata.get(AuthenticationField.API_KEY_ID_KEY))); + assertThat(apiKeyMap, hasKey("name")); // must be present, even if null or non-null + assertThat(apiKeyMap.get("name"), equalTo(authMetadata.get(AuthenticationField.API_KEY_NAME_KEY))); // null or non-null if (apiKeyMetadata == null || apiKeyMetadata.isEmpty()) { assertNull(apiKeyMap.get("metadata")); } else { @@ -371,26 +368,22 @@ public void testApiKeyPopulation() throws Exception { public void testWillNotOverwriteExistingApiKeyAndRealm() throws Exception { User user = new User(randomAlphaOfLengthBetween(4, 12), null, null); Authentication.RealmRef realmRef = new Authentication.RealmRef( - ApiKeyService.API_KEY_REALM_NAME, - ApiKeyService.API_KEY_REALM_TYPE, + AuthenticationField.API_KEY_REALM_NAME, + AuthenticationField.API_KEY_REALM_TYPE, "_node_name" ); - final Map authMetadata = new HashMap<>( - Map.of( - ApiKeyService.API_KEY_ID_KEY, - "api_key_id", - ApiKeyService.API_KEY_NAME_KEY, - "api_key_name", - ApiKeyService.API_KEY_CREATOR_REALM_NAME, - "creator_realm_name", - ApiKeyService.API_KEY_CREATOR_REALM_TYPE, - "creator_realm_type" - ) - ); + final Map authMetadata = new HashMap<>(); + authMetadata.put(AuthenticationField.API_KEY_ID_KEY, "api_key_id"); + authMetadata.put(AuthenticationField.API_KEY_NAME_KEY, randomBoolean() ? null : "api_key_name"); + authMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_NAME, "creator_realm_name"); + authMetadata.put(AuthenticationField.API_KEY_CREATOR_REALM_TYPE, "creator_realm_type"); final Map apiKeyMetadata = ApiKeyTests.randomMetadata(); if (apiKeyMetadata != null) { - authMetadata.put(ApiKeyService.API_KEY_METADATA_KEY, XContentTestUtils.convertToXContent(apiKeyMetadata, XContentType.JSON)); + authMetadata.put( + AuthenticationField.API_KEY_METADATA_KEY, + XContentTestUtils.convertToXContent(apiKeyMetadata, XContentType.JSON) + ); } Authentication auth = new Authentication(user, realmRef, null, Version.CURRENT, AuthenticationType.API_KEY, authMetadata); diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/10_basic.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/10_basic.yml index 46acbe9569f00..b575ddccb449a 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/10_basic.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/10_basic.yml @@ -124,6 +124,8 @@ teardown: - length: { roles: 0 } - match: { authentication_realm.name: "_es_api_key" } - match: { authentication_realm.type: "_es_api_key" } + - match: { api_key.id: "${api_key_id}" } + - match: { api_key.name: "my-api-key" } - do: security.clear_api_key_cache: diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/authenticate/10_basic.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/authenticate/10_basic.yml index 9cc6634c6103e..58586c03b978b 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/authenticate/10_basic.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/authenticate/10_basic.yml @@ -36,3 +36,4 @@ teardown: - match: { roles.0: "superuser" } - match: { full_name: "Authenticate User" } - match: { authentication_type: "realm" } + - match: { api_key: null }