From 27a0b3c52ff2273409fc2de83d8b52cae01acd46 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 28 Feb 2022 16:32:57 +1100 Subject: [PATCH] Fix isApiKey test and apply it consistently (#84396) Creating tokens using API keys is not properly supported till #80926. Previously the created token always has no previlege. Now the token has the same privilege as the API key itself (similar to user created tokens). Authenticating using the token is considered equivalent to the API key itself. Therefore the "isApiKey" check needs to be updated to cater for both authentications of API key itself and the token created by the API key. This PR updates the isApiKey check and apply it consistently to ensure the behaviour is consistent between an API key and a token created by it. The only exception is for supporting run-as. API key itself can run-as another user. But a token created by the API key cannot perform run-as (#84336) similar to how user/token works. --- .../xpack/core/security/SecurityContext.java | 2 +- .../core/security/authc/Authentication.java | 24 ++++++--- .../ManageOwnApiKeyClusterPrivilegeTests.java | 3 +- ...rtSamlInitiateSingleSignOnActionTests.java | 8 ++- .../security/authc/ApiKeyIntegTests.java | 20 ++++++- .../xpack/security/authc/RunAsIntegTests.java | 28 +++++++++- .../authc/apikey/ApiKeySingleNodeTests.java | 53 +++++++++++++++++++ .../xpack/security/authc/ApiKeyService.java | 8 +-- .../security/authz/AuthorizationService.java | 2 +- .../security/authc/ApiKeyServiceTests.java | 13 +++-- .../xpack/security/authz/RBACEngineTests.java | 16 ++++-- 11 files changed, 153 insertions(+), 24 deletions(-) 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 a7c9071ef9c25..e8f390c57380d 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 @@ -187,7 +187,7 @@ public void executeAfterRewritingAuthentication(Consumer consumer private Map rewriteMetadataForApiKeyRoleDescriptors(Version streamVersion, Authentication authentication) { Map metadata = authentication.getMetadata(); // If authentication type is API key, regardless whether it has run-as, the metadata must contain API key role descriptors - if (authentication.isAuthenticatedWithApiKey()) { + if (authentication.isAuthenticatedAsApiKey()) { if (authentication.getVersion().onOrAfter(VERSION_API_KEY_ROLES_AS_BYTES) && streamVersion.before(VERSION_API_KEY_ROLES_AS_BYTES)) { metadata = new HashMap<>(metadata); 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 3f49b2227c617..0485f23a82349 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 @@ -121,22 +121,34 @@ public boolean isAuthenticatedWithServiceAccount() { return ServiceAccountSettings.REALM_TYPE.equals(getAuthenticatedBy().getType()); } - public boolean isAuthenticatedWithApiKey() { - return AuthenticationType.API_KEY.equals(getAuthenticationType()); + /** + * Whether the authenticating user is an API key, including a simple API key or a token created by an API key. + * @return + */ + public boolean isAuthenticatedAsApiKey() { + final boolean result = AuthenticationField.API_KEY_REALM_TYPE.equals(getAuthenticatedBy().getType()); + assert false == result || AuthenticationField.API_KEY_REALM_NAME.equals(getAuthenticatedBy().getName()); + return result; } /** * Authenticate with a service account and no run-as */ public boolean isServiceAccount() { - return isAuthenticatedWithServiceAccount() && false == getUser().isRunAs(); + final boolean result = ServiceAccountSettings.REALM_TYPE.equals(getSourceRealm().getType()); + assert false == result || ServiceAccountSettings.REALM_NAME.equals(getSourceRealm().getName()) + : "service account realm name mismatch"; + return result; } /** - * Authenticated with an API key and no run-as + * Whether the effective user is an API key, this including a simple API key authentication + * or a token created by the API key. */ public boolean isApiKey() { - return isAuthenticatedWithApiKey() && false == getUser().isRunAs(); + final boolean result = AuthenticationField.API_KEY_REALM_TYPE.equals(getSourceRealm().getType()); + assert false == result || AuthenticationField.API_KEY_REALM_NAME.equals(getSourceRealm().getName()) : "api key realm name mismatch"; + return result; } /** @@ -292,7 +304,7 @@ public void toXContentFragment(XContentBuilder builder) throws IOException { } private void assertApiKeyMetadata() { - assert (false == isAuthenticatedWithApiKey()) || (this.metadata.get(AuthenticationField.API_KEY_ID_KEY) != null) + assert (false == isAuthenticatedAsApiKey()) || (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."; } 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 267461cbf73ce..13c40a356022c 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 @@ -187,8 +187,9 @@ private Authentication createMockAuthentication( when(authentication.getSourceRealm()).thenReturn(authenticatedBy); when(authentication.getAuthenticationType()).thenReturn(authenticationType); when(authenticatedBy.getName()).thenReturn(realmName); + when(authenticatedBy.getType()).thenReturn(realmName); when(authentication.getMetadata()).thenReturn(metadata); - when(authentication.isAuthenticatedWithApiKey()).thenCallRealMethod(); + when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod(); when(authentication.isApiKey()).thenCallRealMethod(); return authentication; } diff --git a/x-pack/plugin/identity-provider/src/test/java/org/elasticsearch/xpack/idp/action/TransportSamlInitiateSingleSignOnActionTests.java b/x-pack/plugin/identity-provider/src/test/java/org/elasticsearch/xpack/idp/action/TransportSamlInitiateSingleSignOnActionTests.java index ca18d12939a7e..5246a55056dbd 100644 --- a/x-pack/plugin/identity-provider/src/test/java/org/elasticsearch/xpack/idp/action/TransportSamlInitiateSingleSignOnActionTests.java +++ b/x-pack/plugin/identity-provider/src/test/java/org/elasticsearch/xpack/idp/action/TransportSamlInitiateSingleSignOnActionTests.java @@ -6,6 +6,7 @@ */ package org.elasticsearch.xpack.idp.action; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.PlainActionFuture; @@ -19,6 +20,7 @@ import org.elasticsearch.transport.TransportService; 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.authc.support.SecondaryAuthentication; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.idp.privileges.ServiceProviderPrivileges; @@ -40,6 +42,7 @@ import java.time.Duration; import java.util.Collections; import java.util.HashMap; +import java.util.Map; import java.util.Set; import static org.hamcrest.CoreMatchers.containsString; @@ -162,7 +165,10 @@ private TransportSamlInitiateSingleSignOnAction setupTransportAction(boolean wit true ), new Authentication.RealmRef("_es_api_key", "_es_api_key", "node_name"), - new Authentication.RealmRef("_es_api_key", "_es_api_key", "node_name") + new Authentication.RealmRef("_es_api_key", "_es_api_key", "node_name"), + Version.CURRENT, + Authentication.AuthenticationType.API_KEY, + Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLength(20)) ) ).writeToContext(threadContext); } diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java index 2ba3035e4993e..7397210b6b94d 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/ApiKeyIntegTests.java @@ -52,6 +52,9 @@ import org.elasticsearch.xpack.core.security.action.apikey.InvalidateApiKeyAction; import org.elasticsearch.xpack.core.security.action.apikey.InvalidateApiKeyRequest; import org.elasticsearch.xpack.core.security.action.apikey.InvalidateApiKeyResponse; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequestBuilder; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenResponse; import org.elasticsearch.xpack.core.security.action.user.PutUserAction; import org.elasticsearch.xpack.core.security.action.user.PutUserRequest; import org.elasticsearch.xpack.core.security.action.user.PutUserResponse; @@ -109,6 +112,7 @@ public Settings nodeSettings(int nodeOrdinal, Settings otherSettings) { return Settings.builder() .put(super.nodeSettings(nodeOrdinal, otherSettings)) .put(XPackSettings.API_KEY_SERVICE_ENABLED_SETTING.getKey(), true) + .put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true) .put(ApiKeyService.DELETE_INTERVAL.getKey(), TimeValue.timeValueMillis(DELETE_INTERVAL_MILLIS)) .put(ApiKeyService.DELETE_TIMEOUT.getKey(), TimeValue.timeValueSeconds(5L)) .put("xpack.security.crypto.thread_pool.queue_size", CRYPTO_THREAD_POOL_QUEUE_SIZE) @@ -1111,7 +1115,9 @@ public void testDerivedKeys() throws ExecutionException, InterruptedException { Collections.singletonMap("Authorization", basicAuthHeaderValue(ES_TEST_ROOT_USER, TEST_PASSWORD_SECURE_STRING)) ); final CreateApiKeyResponse response = new CreateApiKeyRequestBuilder(client).setName("key-1") - .setRoleDescriptors(Collections.singletonList(new RoleDescriptor("role", new String[] { "manage_api_key" }, null, null))) + .setRoleDescriptors( + Collections.singletonList(new RoleDescriptor("role", new String[] { "manage_api_key", "manage_token" }, null, null)) + ) .setMetadata(ApiKeyTests.randomMetadata()) .get(); @@ -1122,7 +1128,17 @@ public void testDerivedKeys() throws ExecutionException, InterruptedException { // use the first ApiKey for authorized action final String base64ApiKeyKeyValue = Base64.getEncoder() .encodeToString((response.getId() + ":" + response.getKey().toString()).getBytes(StandardCharsets.UTF_8)); - final Client clientKey1 = client().filterWithHeader(Collections.singletonMap("Authorization", "ApiKey " + base64ApiKeyKeyValue)); + + final Client clientKey1; + if (randomBoolean()) { + clientKey1 = client().filterWithHeader(Collections.singletonMap("Authorization", "ApiKey " + base64ApiKeyKeyValue)); + } else { + final CreateTokenResponse createTokenResponse = new CreateTokenRequestBuilder( + client().filterWithHeader(Collections.singletonMap("Authorization", "ApiKey " + base64ApiKeyKeyValue)), + CreateTokenAction.INSTANCE + ).setGrantType("client_credentials").get(); + clientKey1 = client().filterWithHeader(Map.of("Authorization", "Bearer " + createTokenResponse.getTokenString())); + } final String expectedMessage = "creating derived api keys requires an explicit role descriptor that is empty"; diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java index 55fe797c27090..35b7645b5a4aa 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/RunAsIntegTests.java @@ -157,7 +157,7 @@ public void testRunAsUsingApiKey() throws IOException { createApiKeyResponse.getEntity().getContent() ); - final boolean runAsTestUser = false; + final boolean runAsTestUser = randomBoolean(); final Request authenticateRequest = new Request("GET", "/_security/_authenticate"); authenticateRequest.setOptions( @@ -194,6 +194,32 @@ public void testRunAsUsingApiKey() throws IOException { final ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest(getUserRequest)); assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403)); } + + // Run-as ignored if using a token created by the API key + final Request createTokenRequest = new Request("POST", "/_security/oauth2/token"); + createTokenRequest.setOptions( + createTokenRequest.getOptions().toBuilder().addHeader("Authorization", "ApiKey " + apiKeyMapView.get("encoded")) + ); + createTokenRequest.setJsonEntity("{\"grant_type\":\"client_credentials\"}"); + final Response createTokenResponse = getRestClient().performRequest(createTokenRequest); + final XContentTestUtils.JsonMapView createTokenJsonView = XContentTestUtils.createJsonMapView( + createTokenResponse.getEntity().getContent() + ); + + authenticateRequest.setOptions( + RequestOptions.DEFAULT.toBuilder() + .addHeader("Authorization", "Bearer " + createTokenJsonView.get("access_token")) + .addHeader( + AuthenticationServiceField.RUN_AS_USER_HEADER, + runAsTestUser ? SecuritySettingsSource.TEST_USER_NAME : NO_ROLE_USER + ) + ); + final Response authenticateResponse2 = getRestClient().performRequest(authenticateRequest); + final XContentTestUtils.JsonMapView authenticateJsonView2 = XContentTestUtils.createJsonMapView( + authenticateResponse2.getEntity().getContent() + ); + // run-as header is ignored, the user is still the run_as_user + assertThat(authenticateJsonView2.get("username"), equalTo(RUN_AS_USER)); } public void testRunAsIgnoredForOAuthToken() throws IOException { diff --git a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java index 609a8d96d08f2..2634590b77084 100644 --- a/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java +++ b/x-pack/plugin/security/src/internalClusterTest/java/org/elasticsearch/xpack/security/authc/apikey/ApiKeySingleNodeTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.get.GetResponse; import org.elasticsearch.action.main.MainAction; import org.elasticsearch.action.main.MainRequest; +import org.elasticsearch.client.internal.Client; import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.SecureString; import org.elasticsearch.common.settings.Settings; @@ -27,6 +28,9 @@ import org.elasticsearch.xpack.core.security.action.apikey.CreateApiKeyAction; import org.elasticsearch.xpack.core.security.action.apikey.CreateApiKeyRequest; import org.elasticsearch.xpack.core.security.action.apikey.CreateApiKeyResponse; +import org.elasticsearch.xpack.core.security.action.apikey.GetApiKeyAction; +import org.elasticsearch.xpack.core.security.action.apikey.GetApiKeyRequest; +import org.elasticsearch.xpack.core.security.action.apikey.GetApiKeyResponse; import org.elasticsearch.xpack.core.security.action.apikey.GrantApiKeyAction; import org.elasticsearch.xpack.core.security.action.apikey.GrantApiKeyRequest; import org.elasticsearch.xpack.core.security.action.apikey.QueryApiKeyAction; @@ -35,6 +39,9 @@ import org.elasticsearch.xpack.core.security.action.service.CreateServiceAccountTokenAction; import org.elasticsearch.xpack.core.security.action.service.CreateServiceAccountTokenRequest; import org.elasticsearch.xpack.core.security.action.service.CreateServiceAccountTokenResponse; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequestBuilder; +import org.elasticsearch.xpack.core.security.action.token.CreateTokenResponse; import org.elasticsearch.xpack.core.security.action.user.PutUserAction; import org.elasticsearch.xpack.core.security.action.user.PutUserRequest; import org.elasticsearch.xpack.core.security.authc.support.Hasher; @@ -45,6 +52,7 @@ import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Base64; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -60,6 +68,7 @@ public class ApiKeySingleNodeTests extends SecuritySingleNodeTestCase { protected Settings nodeSettings() { Settings.Builder builder = Settings.builder().put(super.nodeSettings()); builder.put(XPackSettings.API_KEY_SERVICE_ENABLED_SETTING.getKey(), true); + builder.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true); return builder.build(); } @@ -185,6 +194,50 @@ public void testServiceAccountApiKey() throws IOException { assertThat(roleDescriptor, equalTo(ServiceAccountService.getServiceAccounts().get("elastic/fleet-server").roleDescriptor())); } + public void testGetApiKeyWorksForTheApiKeyItself() { + final String apiKeyName = randomAlphaOfLength(10); + final CreateApiKeyResponse createApiKeyResponse = client().execute( + CreateApiKeyAction.INSTANCE, + new CreateApiKeyRequest( + apiKeyName, + List.of(new RoleDescriptor("x", new String[] { "manage_own_api_key", "manage_token" }, null, null, null, null, null, null)), + null, + null + ) + ).actionGet(); + + final String apiKeyId = createApiKeyResponse.getId(); + final String base64ApiKeyKeyValue = Base64.getEncoder() + .encodeToString((apiKeyId + ":" + createApiKeyResponse.getKey().toString()).getBytes(StandardCharsets.UTF_8)); + + // Works for both the API key itself or the token created by it + final Client clientKey1; + if (randomBoolean()) { + clientKey1 = client().filterWithHeader(Collections.singletonMap("Authorization", "ApiKey " + base64ApiKeyKeyValue)); + } else { + final CreateTokenResponse createTokenResponse = new CreateTokenRequestBuilder( + client().filterWithHeader(Collections.singletonMap("Authorization", "ApiKey " + base64ApiKeyKeyValue)), + CreateTokenAction.INSTANCE + ).setGrantType("client_credentials").get(); + clientKey1 = client().filterWithHeader(Map.of("Authorization", "Bearer " + createTokenResponse.getTokenString())); + } + + // Can get its own info + final GetApiKeyResponse getApiKeyResponse = clientKey1.execute( + GetApiKeyAction.INSTANCE, + GetApiKeyRequest.usingApiKeyId(apiKeyId, randomBoolean()) + ).actionGet(); + assertThat(getApiKeyResponse.getApiKeyInfos().length, equalTo(1)); + assertThat(getApiKeyResponse.getApiKeyInfos()[0].getId(), equalTo(apiKeyId)); + + // Cannot get any other keys + final ElasticsearchSecurityException e = expectThrows( + ElasticsearchSecurityException.class, + () -> clientKey1.execute(GetApiKeyAction.INSTANCE, GetApiKeyRequest.forAllApiKeys()).actionGet() + ); + assertThat(e.getMessage(), containsString("unauthorized for API key id [" + apiKeyId + "]")); + } + private Map getApiKeyDocument(String apiKeyId) { final GetResponse getResponse = client().execute(GetAction.INSTANCE, new GetRequest(".security-7", apiKeyId)).actionGet(); return getResponse.getSource(); 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 23e87db5c2d25..434389bd729ef 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 @@ -1371,10 +1371,12 @@ public static String getCreatorRealmType(final Authentication authentication) { * @return A map for the metadata or an empty map if no metadata is found. */ public static Map getApiKeyMetadata(Authentication authentication) { - if (false == authentication.isAuthenticatedWithApiKey()) { + if (false == authentication.isAuthenticatedAsApiKey()) { throw new IllegalArgumentException( - "authentication type must be [api_key], got [" - + authentication.getAuthenticationType().name().toLowerCase(Locale.ROOT) + "authentication realm must be [" + + AuthenticationField.API_KEY_REALM_TYPE + + "], got [" + + authentication.getAuthenticatedBy().getType() + "]" ); } 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 6bf84a7884d6f..72484ff5e32b4 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 @@ -879,7 +879,7 @@ private ElasticsearchSecurityException denialException( userText = userText + " run as [" + authentication.getUser().principal() + "]"; } // check for authentication by API key - if (authentication.isAuthenticatedWithApiKey()) { + if (authentication.isAuthenticatedAsApiKey()) { 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; diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java index af6cdd19209c9..479bfb2cb4c77 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java @@ -1234,7 +1234,7 @@ public void testGetCreatorRealm() { assertThat(ApiKeyService.getCreatorRealmName(authentication2), equalTo(lookupRealmRef.getName())); assertThat(ApiKeyService.getCreatorRealmType(authentication2), equalTo(lookupRealmRef.getType())); - // Realm run-as + // Realm final Authentication authentication3 = AuthenticationTests.randomRealmAuthentication(); assertThat(ApiKeyService.getCreatorRealmName(authentication3), equalTo(authentication3.getSourceRealm().getName())); assertThat(ApiKeyService.getCreatorRealmType(authentication3), equalTo(authentication3.getSourceRealm().getType())); @@ -1464,7 +1464,10 @@ public void testApiKeyDocDeserializationWithNullValues() throws IOException { public void testGetApiKeyMetadata() throws IOException { final Authentication apiKeyAuthentication = mock(Authentication.class); when(apiKeyAuthentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY); - when(apiKeyAuthentication.isAuthenticatedWithApiKey()).thenCallRealMethod(); + when(apiKeyAuthentication.getAuthenticatedBy()).thenReturn( + new RealmRef(AuthenticationField.API_KEY_REALM_NAME, AuthenticationField.API_KEY_REALM_TYPE, randomAlphaOfLengthBetween(3, 8)) + ); + when(apiKeyAuthentication.isAuthenticatedAsApiKey()).thenCallRealMethod(); final Map apiKeyMetadata = ApiKeyTests.randomMetadata(); if (apiKeyMetadata == null) { when(apiKeyAuthentication.getMetadata()).thenReturn(Map.of()); @@ -1481,14 +1484,14 @@ public void testGetApiKeyMetadata() throws IOException { } final Authentication authentication = mock(Authentication.class); - when(authentication.getAuthenticationType()).thenReturn( - randomValueOtherThan(AuthenticationType.API_KEY, () -> randomFrom(AuthenticationType.values())) + when(authentication.getAuthenticatedBy()).thenReturn( + new RealmRef(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(3, 8)) ); final IllegalArgumentException e = expectThrows( IllegalArgumentException.class, () -> ApiKeyService.getApiKeyMetadata(authentication) ); - assertThat(e.getMessage(), containsString("authentication type must be [api_key]")); + assertThat(e.getMessage(), containsString("authentication realm must be [_es_api_key]")); } public static class Utils { 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 0aa5aeabb8d36..82cc0ec14cc40 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 @@ -336,11 +336,14 @@ public void testSameUserPermissionAllowsSelfApiKeyInfoRetrievalWhenAuthenticated final TransportRequest request = GetApiKeyRequest.usingApiKeyId(apiKeyId, false); final Authentication authentication = mock(Authentication.class); final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class); + when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME); + when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE); when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY); + when(authentication.getSourceRealm()).thenReturn(authenticatedBy); when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, apiKeyId)); - when(authentication.isAuthenticatedWithApiKey()).thenCallRealMethod(); + when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod(); when(authentication.isApiKey()).thenCallRealMethod(); assertTrue(engine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication)); @@ -354,9 +357,11 @@ public void testSameUserPermissionDeniesApiKeyInfoRetrievalWhenAuthenticatedByAD final Authentication.RealmRef authenticatedBy = mock(Authentication.RealmRef.class); when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME); when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE); + when(authentication.getSourceRealm()).thenReturn(authenticatedBy); when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); - when(authentication.isAuthenticatedWithApiKey()).thenCallRealMethod(); + when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod(); when(authentication.isApiKey()).thenCallRealMethod(); assertFalse(engine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication)); @@ -371,10 +376,15 @@ public void testSameUserPermissionDeniesApiKeyInfoRetrievalWhenLookedupByIsPrese final Authentication.RealmRef lookedupBy = mock(Authentication.RealmRef.class); when(authentication.getUser()).thenReturn(user); when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getName()).thenReturn(AuthenticationField.API_KEY_REALM_NAME); + when(authenticatedBy.getType()).thenReturn(AuthenticationField.API_KEY_REALM_TYPE); when(authentication.getLookedUpBy()).thenReturn(lookedupBy); + when(lookedupBy.getName()).thenReturn("name"); + when(lookedupBy.getType()).thenReturn("type"); + when(authentication.getSourceRealm()).thenReturn(lookedupBy); when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.API_KEY); when(authentication.getMetadata()).thenReturn(Map.of(AuthenticationField.API_KEY_ID_KEY, randomAlphaOfLengthBetween(4, 7))); - when(authentication.isAuthenticatedWithApiKey()).thenCallRealMethod(); + when(authentication.isAuthenticatedAsApiKey()).thenCallRealMethod(); when(authentication.isApiKey()).thenCallRealMethod(); assertFalse(engine.checkSameUserPermissions(GetApiKeyAction.NAME, request, authentication));